├── .gitignore ├── Example ├── Podfile ├── Podfile.lock ├── SandBoxPreviewTool.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── SandBoxPreviewTool.xcworkspace │ └── contents.xcworkspacedata ├── SandBoxPreviewTool │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── SPAppDelegate.h │ ├── SPAppDelegate.m │ ├── SPViewController.h │ ├── SPViewController.m │ ├── SandBoxPreviewTool-Info.plist │ ├── SandBoxPreviewTool-Prefix.pch │ ├── en.lproj │ │ └── InfoPlist.strings │ └── main.m └── Tests │ ├── Tests-Info.plist │ ├── Tests-Prefix.pch │ ├── Tests.m │ └── en.lproj │ └── InfoPlist.strings ├── LICENSE ├── README.md ├── SandBoxPreviewTool.podspec ├── SandBoxPreviewTool ├── Assets │ ├── .gitkeep │ └── SandBoxPreviewTool.bundle │ │ ├── GenericFolderIcon.png │ │ ├── back_icon@2x.png │ │ ├── back_icon@3x.png │ │ ├── lj_Pictures.png │ │ └── lj_unknow_icon.png └── Classes │ ├── .gitkeep │ ├── LJAlertView.h │ ├── LJAlertView.m │ ├── LJ_DirToolNavigatorController.h │ ├── LJ_DirToolNavigatorController.m │ ├── LJ_FileInfo.h │ ├── LJ_FileInfo.m │ ├── LJ_FileInfoController.h │ ├── LJ_FileInfoController.m │ ├── LJ_HomeDirViewController.h │ ├── LJ_HomeDirViewController.m │ ├── SLUnilityObject.h │ ├── SLUnilityObject.m │ ├── SandBoxPreviewTool.h │ ├── SandBoxPreviewTool.m │ ├── SuspensionButton.h │ └── SuspensionButton.m └── _Pods.xcodeproj /.gitignore: -------------------------------------------------------------------------------- 1 | ######################### 2 | 3 | #CocoaPods 4 | Pods/ 5 | 6 | # .gitignore file for Xcode4 and Xcode5 Source projects 7 | # 8 | # Apple bugs, waiting for Apple to fix/respond: 9 | # 10 | # 15564624 - what does the xccheckout file in Xcode5 do? Where's the documentation? 11 | # 12 | # Version 2.6 13 | # For latest version, see: http://stackoverflow.com/questions/49478/git-ignore-file-for-xcode-projects 14 | # 15 | # 2015 updates: 16 | # - Fixed typo in "xccheckout" line - thanks to @lyck for pointing it out! 17 | # - Fixed the .idea optional ignore. Thanks to @hashier for pointing this out 18 | # - Finally added "xccheckout" to the ignore. Apple still refuses to answer support requests about this, but in practice it seems you should ignore it. 19 | # - minor tweaks from Jona and Coeur (slightly more precise xc* filtering/names) 20 | # 2014 updates: 21 | # - appended non-standard items DISABLED by default (uncomment if you use those tools) 22 | # - removed the edit that an SO.com moderator made without bothering to ask me 23 | # - researched CocoaPods .lock more carefully, thanks to Gokhan Celiker 24 | # 2013 updates: 25 | # - fixed the broken "save personal Schemes" 26 | # - added line-by-line explanations for EVERYTHING (some were missing) 27 | # 28 | # NB: if you are storing "built" products, this WILL NOT WORK, 29 | # and you should use a different .gitignore (or none at all) 30 | # This file is for SOURCE projects, where there are many extra 31 | # files that we want to exclude 32 | # 33 | ######################### 34 | 35 | ##### 36 | # OS X temporary files that should never be committed 37 | # 38 | # c.f. http://www.westwind.com/reference/os-x/invisibles.html 39 | 40 | .DS_Store 41 | 42 | # c.f. http://www.westwind.com/reference/os-x/invisibles.html 43 | 44 | .Trashes 45 | 46 | # c.f. http://www.westwind.com/reference/os-x/invisibles.html 47 | 48 | *.swp 49 | 50 | # 51 | # *.lock - this is used and abused by many editors for many different things. 52 | # For the main ones I use (e.g. Eclipse), it should be excluded 53 | # from source-control, but YMMV. 54 | # (lock files are usually local-only file-synchronization on the local FS that should NOT go in git) 55 | # c.f. the "OPTIONAL" section at bottom though, for tool-specific variations! 56 | # 57 | # In particular, if you're using CocoaPods, you'll want to comment-out this line: 58 | #*.lock 59 | 60 | 61 | # 62 | # profile - REMOVED temporarily (on double-checking, I can't find it in OS X docs?) 63 | #profile 64 | 65 | 66 | #### 67 | # Xcode temporary files that should never be committed 68 | # 69 | # NB: NIB/XIB files still exist even on Storyboard projects, so we want this... 70 | 71 | *~.nib 72 | 73 | 74 | #### 75 | # Xcode build files - 76 | # 77 | # NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "DerivedData" 78 | 79 | DerivedData/ 80 | 81 | # NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "build" 82 | 83 | build/ 84 | 85 | 86 | ##### 87 | # Xcode private settings (window sizes, bookmarks, breakpoints, custom executables, smart groups) 88 | # 89 | # This is complicated: 90 | # 91 | # SOMETIMES you need to put this file in version control. 92 | # Apple designed it poorly - if you use "custom executables", they are 93 | # saved in this file. 94 | # 99% of projects do NOT use those, so they do NOT want to version control this file. 95 | # ..but if you're in the 1%, comment out the line "*.pbxuser" 96 | 97 | # .pbxuser: http://lists.apple.com/archives/xcode-users/2004/Jan/msg00193.html 98 | 99 | *.pbxuser 100 | 101 | # .mode1v3: http://lists.apple.com/archives/xcode-users/2007/Oct/msg00465.html 102 | 103 | *.mode1v3 104 | 105 | # .mode2v3: http://lists.apple.com/archives/xcode-users/2007/Oct/msg00465.html 106 | 107 | *.mode2v3 108 | 109 | # .perspectivev3: http://stackoverflow.com/questions/5223297/xcode-projects-what-is-a-perspectivev3-file 110 | 111 | *.perspectivev3 112 | 113 | # NB: also, whitelist the default ones, some projects need to use these 114 | !default.pbxuser 115 | !default.mode1v3 116 | !default.mode2v3 117 | !default.perspectivev3 118 | 119 | 120 | #### 121 | # Xcode 4 - semi-personal settings 122 | # 123 | # Apple Shared data that Apple put in the wrong folder 124 | # c.f. http://stackoverflow.com/a/19260712/153422 125 | # FROM ANSWER: Apple says "don't ignore it" 126 | # FROM COMMENTS: Apple is wrong; Apple code is too buggy to trust; there are no known negative side-effects to ignoring Apple's unofficial advice and instead doing the thing that actively fixes bugs in Xcode 127 | # Up to you, but ... current advice: ignore it. 128 | *.xccheckout 129 | 130 | # 131 | # 132 | # OPTION 1: --------------------------------- 133 | # throw away ALL personal settings (including custom schemes! 134 | # - unless they are "shared") 135 | # As per build/ and DerivedData/, this ought to have a trailing slash 136 | # 137 | # NB: this is exclusive with OPTION 2 below 138 | xcuserdata/ 139 | xcshareddata/ 140 | 141 | # OPTION 2: --------------------------------- 142 | # get rid of ALL personal settings, but KEEP SOME OF THEM 143 | # - NB: you must manually uncomment the bits you want to keep 144 | # 145 | # NB: this *requires* git v1.8.2 or above; you may need to upgrade to latest OS X, 146 | # or manually install git over the top of the OS X version 147 | # NB: this is exclusive with OPTION 1 above 148 | # 149 | #xcuserdata/**/* 150 | 151 | # (requires option 2 above): Personal Schemes 152 | # 153 | #!xcuserdata/**/xcschemes/* 154 | 155 | #### 156 | # XCode 4 workspaces - more detailed 157 | # 158 | # Workspaces are important! They are a core feature of Xcode - don't exclude them :) 159 | # 160 | # Workspace layout is quite spammy. For reference: 161 | # 162 | # /(root)/ 163 | # /(project-name).xcodeproj/ 164 | # project.pbxproj 165 | # /project.xcworkspace/ 166 | # contents.xcworkspacedata 167 | # /xcuserdata/ 168 | # /(your name)/xcuserdatad/ 169 | # UserInterfaceState.xcuserstate 170 | # /xcshareddata/ 171 | # /xcschemes/ 172 | # (shared scheme name).xcscheme 173 | # /xcuserdata/ 174 | # /(your name)/xcuserdatad/ 175 | # (private scheme).xcscheme 176 | # xcschememanagement.plist 177 | # 178 | # 179 | 180 | #### 181 | # Xcode 4 - Deprecated classes 182 | # 183 | # Allegedly, if you manually "deprecate" your classes, they get moved here. 184 | # 185 | # We're using source-control, so this is a "feature" that we do not want! 186 | 187 | *.moved-aside 188 | 189 | #### 190 | # OPTIONAL: Some well-known tools that people use side-by-side with Xcode / iOS development 191 | # 192 | # NB: I'd rather not include these here, but gitignore's design is weak and doesn't allow 193 | # modular gitignore: you have to put EVERYTHING in one file. 194 | # 195 | # COCOAPODS: 196 | # 197 | # c.f. http://guides.cocoapods.org/using/using-cocoapods.html#what-is-a-podfilelock 198 | # c.f. http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 199 | # 200 | #!Podfile.lock 201 | # 202 | # RUBY: 203 | # 204 | # c.f. http://yehudakatz.com/2010/12/16/clarifying-the-roles-of-the-gemspec-and-gemfile/ 205 | # 206 | #!Gemfile.lock 207 | # 208 | # IDEA: 209 | # 210 | # c.f. https://www.jetbrains.com/objc/help/managing-projects-under-version-control.html?search=workspace.xml 211 | # 212 | #.idea/workspace.xml 213 | # 214 | # TEXTMATE: 215 | # 216 | # -- UNVERIFIED: c.f. http://stackoverflow.com/a/50283/153422 217 | # 218 | #tm_build_errors 219 | 220 | #### 221 | # UNKNOWN: recommended by others, but I can't discover what these files are 222 | # 223 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | use_frameworks! 2 | 3 | platform :ios, '9.0' 4 | 5 | target 'SandBoxPreviewTool_Example' do 6 | pod 'SandBoxPreviewTool', :path => '../' 7 | 8 | target 'SandBoxPreviewTool_Tests' do 9 | inherit! :search_paths 10 | 11 | pod 'Specta' 12 | pod 'Expecta' 13 | pod 'FBSnapshotTestCase' 14 | pod 'Expecta+Snapshots' 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Expecta (1.0.6) 3 | - "Expecta+Snapshots (3.1.1)": 4 | - Expecta (~> 1.0) 5 | - FBSnapshotTestCase/Core (~> 2.0) 6 | - Specta (~> 1.0) 7 | - FBSnapshotTestCase (2.1.4): 8 | - FBSnapshotTestCase/SwiftSupport (= 2.1.4) 9 | - FBSnapshotTestCase/Core (2.1.4) 10 | - FBSnapshotTestCase/SwiftSupport (2.1.4): 11 | - FBSnapshotTestCase/Core 12 | - SandBoxPreviewTool (1.2) 13 | - Specta (1.0.7) 14 | 15 | DEPENDENCIES: 16 | - Expecta 17 | - "Expecta+Snapshots" 18 | - FBSnapshotTestCase 19 | - SandBoxPreviewTool (from `../`) 20 | - Specta 21 | 22 | SPEC REPOS: 23 | trunk: 24 | - Expecta 25 | - "Expecta+Snapshots" 26 | - FBSnapshotTestCase 27 | - Specta 28 | 29 | EXTERNAL SOURCES: 30 | SandBoxPreviewTool: 31 | :path: "../" 32 | 33 | SPEC CHECKSUMS: 34 | Expecta: 3b6bd90a64b9a1dcb0b70aa0e10a7f8f631667d5 35 | "Expecta+Snapshots": dcff217eef506dabd6dfdc7864ea2da321fafbb8 36 | FBSnapshotTestCase: 094f9f314decbabe373b87cc339bea235a63e07a 37 | SandBoxPreviewTool: f0749cc522d9aac75d73c417148d92703552db29 38 | Specta: 3e1bd89c3517421982dc4d1c992503e48bd5fe66 39 | 40 | PODFILE CHECKSUM: de4d458ee7d40807ebaef6e605d3a4d6468f20e8 41 | 42 | COCOAPODS: 1.10.0 43 | -------------------------------------------------------------------------------- /Example/SandBoxPreviewTool.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 11 | 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58F195388D20070C39A /* CoreGraphics.framework */; }; 12 | 6003F592195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; 13 | 6003F598195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F596195388D20070C39A /* InfoPlist.strings */; }; 14 | 6003F59A195388D20070C39A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F599195388D20070C39A /* main.m */; }; 15 | 6003F59E195388D20070C39A /* SPAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F59D195388D20070C39A /* SPAppDelegate.m */; }; 16 | 6003F5A7195388D20070C39A /* SPViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F5A6195388D20070C39A /* SPViewController.m */; }; 17 | 6003F5A9195388D20070C39A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5A8195388D20070C39A /* Images.xcassets */; }; 18 | 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F5AF195388D20070C39A /* XCTest.framework */; }; 19 | 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 20 | 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; 21 | 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5B8195388D20070C39A /* InfoPlist.strings */; }; 22 | 6003F5BC195388D20070C39A /* Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F5BB195388D20070C39A /* Tests.m */; }; 23 | 71719F9F1E33DC2100824A3D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */; }; 24 | 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */; }; 25 | BD7F4B47F4E1122C2873B0D7 /* Pods_SandBoxPreviewTool_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7481A86265662E0A72220340 /* Pods_SandBoxPreviewTool_Tests.framework */; }; 26 | E30690ED7A9183A2406D4AA7 /* Pods_SandBoxPreviewTool_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 495689BB437DD1A31911A25D /* Pods_SandBoxPreviewTool_Example.framework */; }; 27 | /* End PBXBuildFile section */ 28 | 29 | /* Begin PBXContainerItemProxy section */ 30 | 6003F5B3195388D20070C39A /* PBXContainerItemProxy */ = { 31 | isa = PBXContainerItemProxy; 32 | containerPortal = 6003F582195388D10070C39A /* Project object */; 33 | proxyType = 1; 34 | remoteGlobalIDString = 6003F589195388D20070C39A; 35 | remoteInfo = SandBoxPreviewTool; 36 | }; 37 | /* End PBXContainerItemProxy section */ 38 | 39 | /* Begin PBXFileReference section */ 40 | 0014BB27B1F90467158DF8A7 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 41 | 0187677F179E6903755B51B3 /* Pods-SandBoxPreviewTool_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SandBoxPreviewTool_Example.release.xcconfig"; path = "Target Support Files/Pods-SandBoxPreviewTool_Example/Pods-SandBoxPreviewTool_Example.release.xcconfig"; sourceTree = ""; }; 42 | 495689BB437DD1A31911A25D /* Pods_SandBoxPreviewTool_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SandBoxPreviewTool_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 43 | 4DA473B2CB89C8A392E56D58 /* Pods-SandBoxPreviewTool_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SandBoxPreviewTool_Tests.release.xcconfig"; path = "Target Support Files/Pods-SandBoxPreviewTool_Tests/Pods-SandBoxPreviewTool_Tests.release.xcconfig"; sourceTree = ""; }; 44 | 6003F58A195388D20070C39A /* SandBoxPreviewTool_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SandBoxPreviewTool_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 45 | 6003F58D195388D20070C39A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 46 | 6003F58F195388D20070C39A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 47 | 6003F591195388D20070C39A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 48 | 6003F595195388D20070C39A /* SandBoxPreviewTool-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SandBoxPreviewTool-Info.plist"; sourceTree = ""; }; 49 | 6003F597195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 50 | 6003F599195388D20070C39A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 51 | 6003F59B195388D20070C39A /* SandBoxPreviewTool-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SandBoxPreviewTool-Prefix.pch"; sourceTree = ""; }; 52 | 6003F59C195388D20070C39A /* SPAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPAppDelegate.h; sourceTree = ""; }; 53 | 6003F59D195388D20070C39A /* SPAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPAppDelegate.m; sourceTree = ""; }; 54 | 6003F5A5195388D20070C39A /* SPViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPViewController.h; sourceTree = ""; }; 55 | 6003F5A6195388D20070C39A /* SPViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPViewController.m; sourceTree = ""; }; 56 | 6003F5A8195388D20070C39A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 57 | 6003F5AE195388D20070C39A /* SandBoxPreviewTool_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SandBoxPreviewTool_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 58 | 6003F5AF195388D20070C39A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 59 | 6003F5B7195388D20070C39A /* Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tests-Info.plist"; sourceTree = ""; }; 60 | 6003F5B9195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 61 | 6003F5BB195388D20070C39A /* Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Tests.m; sourceTree = ""; }; 62 | 606FC2411953D9B200FFA9A0 /* Tests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Tests-Prefix.pch"; sourceTree = ""; }; 63 | 71719F9E1E33DC2100824A3D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 64 | 7481A86265662E0A72220340 /* Pods_SandBoxPreviewTool_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SandBoxPreviewTool_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 65 | 75071BA86B19BA1DE6B6628E /* Pods-SandBoxPreviewTool_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SandBoxPreviewTool_Example.debug.xcconfig"; path = "Target Support Files/Pods-SandBoxPreviewTool_Example/Pods-SandBoxPreviewTool_Example.debug.xcconfig"; sourceTree = ""; }; 66 | 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Main.storyboard; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 67 | AEE8C9032A20BCAB80AF04CB /* Pods-SandBoxPreviewTool_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SandBoxPreviewTool_Tests.debug.xcconfig"; path = "Target Support Files/Pods-SandBoxPreviewTool_Tests/Pods-SandBoxPreviewTool_Tests.debug.xcconfig"; sourceTree = ""; }; 68 | CCB2A11A3852DF52BE95252D /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 69 | F50362970147F6E8E025B5F5 /* SandBoxPreviewTool.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = SandBoxPreviewTool.podspec; path = ../SandBoxPreviewTool.podspec; sourceTree = ""; }; 70 | /* End PBXFileReference section */ 71 | 72 | /* Begin PBXFrameworksBuildPhase section */ 73 | 6003F587195388D20070C39A /* Frameworks */ = { 74 | isa = PBXFrameworksBuildPhase; 75 | buildActionMask = 2147483647; 76 | files = ( 77 | 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */, 78 | 6003F592195388D20070C39A /* UIKit.framework in Frameworks */, 79 | 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */, 80 | E30690ED7A9183A2406D4AA7 /* Pods_SandBoxPreviewTool_Example.framework in Frameworks */, 81 | ); 82 | runOnlyForDeploymentPostprocessing = 0; 83 | }; 84 | 6003F5AB195388D20070C39A /* Frameworks */ = { 85 | isa = PBXFrameworksBuildPhase; 86 | buildActionMask = 2147483647; 87 | files = ( 88 | 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */, 89 | 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */, 90 | 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */, 91 | BD7F4B47F4E1122C2873B0D7 /* Pods_SandBoxPreviewTool_Tests.framework in Frameworks */, 92 | ); 93 | runOnlyForDeploymentPostprocessing = 0; 94 | }; 95 | /* End PBXFrameworksBuildPhase section */ 96 | 97 | /* Begin PBXGroup section */ 98 | 6003F581195388D10070C39A = { 99 | isa = PBXGroup; 100 | children = ( 101 | 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */, 102 | 6003F593195388D20070C39A /* Example for SandBoxPreviewTool */, 103 | 6003F5B5195388D20070C39A /* Tests */, 104 | 6003F58C195388D20070C39A /* Frameworks */, 105 | 6003F58B195388D20070C39A /* Products */, 106 | F6F353D21F49564257802BDE /* Pods */, 107 | ); 108 | sourceTree = ""; 109 | }; 110 | 6003F58B195388D20070C39A /* Products */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | 6003F58A195388D20070C39A /* SandBoxPreviewTool_Example.app */, 114 | 6003F5AE195388D20070C39A /* SandBoxPreviewTool_Tests.xctest */, 115 | ); 116 | name = Products; 117 | sourceTree = ""; 118 | }; 119 | 6003F58C195388D20070C39A /* Frameworks */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | 6003F58D195388D20070C39A /* Foundation.framework */, 123 | 6003F58F195388D20070C39A /* CoreGraphics.framework */, 124 | 6003F591195388D20070C39A /* UIKit.framework */, 125 | 6003F5AF195388D20070C39A /* XCTest.framework */, 126 | 495689BB437DD1A31911A25D /* Pods_SandBoxPreviewTool_Example.framework */, 127 | 7481A86265662E0A72220340 /* Pods_SandBoxPreviewTool_Tests.framework */, 128 | ); 129 | name = Frameworks; 130 | sourceTree = ""; 131 | }; 132 | 6003F593195388D20070C39A /* Example for SandBoxPreviewTool */ = { 133 | isa = PBXGroup; 134 | children = ( 135 | 6003F59C195388D20070C39A /* SPAppDelegate.h */, 136 | 6003F59D195388D20070C39A /* SPAppDelegate.m */, 137 | 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */, 138 | 6003F5A5195388D20070C39A /* SPViewController.h */, 139 | 6003F5A6195388D20070C39A /* SPViewController.m */, 140 | 71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */, 141 | 6003F5A8195388D20070C39A /* Images.xcassets */, 142 | 6003F594195388D20070C39A /* Supporting Files */, 143 | ); 144 | name = "Example for SandBoxPreviewTool"; 145 | path = SandBoxPreviewTool; 146 | sourceTree = ""; 147 | }; 148 | 6003F594195388D20070C39A /* Supporting Files */ = { 149 | isa = PBXGroup; 150 | children = ( 151 | 6003F595195388D20070C39A /* SandBoxPreviewTool-Info.plist */, 152 | 6003F596195388D20070C39A /* InfoPlist.strings */, 153 | 6003F599195388D20070C39A /* main.m */, 154 | 6003F59B195388D20070C39A /* SandBoxPreviewTool-Prefix.pch */, 155 | ); 156 | name = "Supporting Files"; 157 | sourceTree = ""; 158 | }; 159 | 6003F5B5195388D20070C39A /* Tests */ = { 160 | isa = PBXGroup; 161 | children = ( 162 | 6003F5BB195388D20070C39A /* Tests.m */, 163 | 6003F5B6195388D20070C39A /* Supporting Files */, 164 | ); 165 | path = Tests; 166 | sourceTree = ""; 167 | }; 168 | 6003F5B6195388D20070C39A /* Supporting Files */ = { 169 | isa = PBXGroup; 170 | children = ( 171 | 6003F5B7195388D20070C39A /* Tests-Info.plist */, 172 | 6003F5B8195388D20070C39A /* InfoPlist.strings */, 173 | 606FC2411953D9B200FFA9A0 /* Tests-Prefix.pch */, 174 | ); 175 | name = "Supporting Files"; 176 | sourceTree = ""; 177 | }; 178 | 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */ = { 179 | isa = PBXGroup; 180 | children = ( 181 | F50362970147F6E8E025B5F5 /* SandBoxPreviewTool.podspec */, 182 | 0014BB27B1F90467158DF8A7 /* README.md */, 183 | CCB2A11A3852DF52BE95252D /* LICENSE */, 184 | ); 185 | name = "Podspec Metadata"; 186 | sourceTree = ""; 187 | }; 188 | F6F353D21F49564257802BDE /* Pods */ = { 189 | isa = PBXGroup; 190 | children = ( 191 | 75071BA86B19BA1DE6B6628E /* Pods-SandBoxPreviewTool_Example.debug.xcconfig */, 192 | 0187677F179E6903755B51B3 /* Pods-SandBoxPreviewTool_Example.release.xcconfig */, 193 | AEE8C9032A20BCAB80AF04CB /* Pods-SandBoxPreviewTool_Tests.debug.xcconfig */, 194 | 4DA473B2CB89C8A392E56D58 /* Pods-SandBoxPreviewTool_Tests.release.xcconfig */, 195 | ); 196 | path = Pods; 197 | sourceTree = ""; 198 | }; 199 | /* End PBXGroup section */ 200 | 201 | /* Begin PBXNativeTarget section */ 202 | 6003F589195388D20070C39A /* SandBoxPreviewTool_Example */ = { 203 | isa = PBXNativeTarget; 204 | buildConfigurationList = 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "SandBoxPreviewTool_Example" */; 205 | buildPhases = ( 206 | 43CA69166FDA28B599F01882 /* [CP] Check Pods Manifest.lock */, 207 | 6003F586195388D20070C39A /* Sources */, 208 | 6003F587195388D20070C39A /* Frameworks */, 209 | 6003F588195388D20070C39A /* Resources */, 210 | F1C9D221B9B18870E0340BEB /* [CP] Embed Pods Frameworks */, 211 | ); 212 | buildRules = ( 213 | ); 214 | dependencies = ( 215 | ); 216 | name = SandBoxPreviewTool_Example; 217 | productName = SandBoxPreviewTool; 218 | productReference = 6003F58A195388D20070C39A /* SandBoxPreviewTool_Example.app */; 219 | productType = "com.apple.product-type.application"; 220 | }; 221 | 6003F5AD195388D20070C39A /* SandBoxPreviewTool_Tests */ = { 222 | isa = PBXNativeTarget; 223 | buildConfigurationList = 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "SandBoxPreviewTool_Tests" */; 224 | buildPhases = ( 225 | B35AEFF6FE2C5929242B2453 /* [CP] Check Pods Manifest.lock */, 226 | 6003F5AA195388D20070C39A /* Sources */, 227 | 6003F5AB195388D20070C39A /* Frameworks */, 228 | 6003F5AC195388D20070C39A /* Resources */, 229 | 1B7580F329644BFB521115C7 /* [CP] Embed Pods Frameworks */, 230 | ); 231 | buildRules = ( 232 | ); 233 | dependencies = ( 234 | 6003F5B4195388D20070C39A /* PBXTargetDependency */, 235 | ); 236 | name = SandBoxPreviewTool_Tests; 237 | productName = SandBoxPreviewToolTests; 238 | productReference = 6003F5AE195388D20070C39A /* SandBoxPreviewTool_Tests.xctest */; 239 | productType = "com.apple.product-type.bundle.unit-test"; 240 | }; 241 | /* End PBXNativeTarget section */ 242 | 243 | /* Begin PBXProject section */ 244 | 6003F582195388D10070C39A /* Project object */ = { 245 | isa = PBXProject; 246 | attributes = { 247 | CLASSPREFIX = SP; 248 | LastUpgradeCheck = 0720; 249 | ORGANIZATIONNAME = "492838605@qq.com"; 250 | TargetAttributes = { 251 | 6003F5AD195388D20070C39A = { 252 | TestTargetID = 6003F589195388D20070C39A; 253 | }; 254 | }; 255 | }; 256 | buildConfigurationList = 6003F585195388D10070C39A /* Build configuration list for PBXProject "SandBoxPreviewTool" */; 257 | compatibilityVersion = "Xcode 3.2"; 258 | developmentRegion = English; 259 | hasScannedForEncodings = 0; 260 | knownRegions = ( 261 | English, 262 | en, 263 | Base, 264 | ); 265 | mainGroup = 6003F581195388D10070C39A; 266 | productRefGroup = 6003F58B195388D20070C39A /* Products */; 267 | projectDirPath = ""; 268 | projectRoot = ""; 269 | targets = ( 270 | 6003F589195388D20070C39A /* SandBoxPreviewTool_Example */, 271 | 6003F5AD195388D20070C39A /* SandBoxPreviewTool_Tests */, 272 | ); 273 | }; 274 | /* End PBXProject section */ 275 | 276 | /* Begin PBXResourcesBuildPhase section */ 277 | 6003F588195388D20070C39A /* Resources */ = { 278 | isa = PBXResourcesBuildPhase; 279 | buildActionMask = 2147483647; 280 | files = ( 281 | 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */, 282 | 71719F9F1E33DC2100824A3D /* LaunchScreen.storyboard in Resources */, 283 | 6003F5A9195388D20070C39A /* Images.xcassets in Resources */, 284 | 6003F598195388D20070C39A /* InfoPlist.strings in Resources */, 285 | ); 286 | runOnlyForDeploymentPostprocessing = 0; 287 | }; 288 | 6003F5AC195388D20070C39A /* Resources */ = { 289 | isa = PBXResourcesBuildPhase; 290 | buildActionMask = 2147483647; 291 | files = ( 292 | 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */, 293 | ); 294 | runOnlyForDeploymentPostprocessing = 0; 295 | }; 296 | /* End PBXResourcesBuildPhase section */ 297 | 298 | /* Begin PBXShellScriptBuildPhase section */ 299 | 1B7580F329644BFB521115C7 /* [CP] Embed Pods Frameworks */ = { 300 | isa = PBXShellScriptBuildPhase; 301 | buildActionMask = 2147483647; 302 | files = ( 303 | ); 304 | inputPaths = ( 305 | "${PODS_ROOT}/Target Support Files/Pods-SandBoxPreviewTool_Tests/Pods-SandBoxPreviewTool_Tests-frameworks.sh", 306 | "${BUILT_PRODUCTS_DIR}/Expecta/Expecta.framework", 307 | "${BUILT_PRODUCTS_DIR}/Expecta+Snapshots/Expecta_Snapshots.framework", 308 | "${BUILT_PRODUCTS_DIR}/FBSnapshotTestCase/FBSnapshotTestCase.framework", 309 | "${BUILT_PRODUCTS_DIR}/Specta/Specta.framework", 310 | ); 311 | name = "[CP] Embed Pods Frameworks"; 312 | outputPaths = ( 313 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Expecta.framework", 314 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Expecta_Snapshots.framework", 315 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBSnapshotTestCase.framework", 316 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Specta.framework", 317 | ); 318 | runOnlyForDeploymentPostprocessing = 0; 319 | shellPath = /bin/sh; 320 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SandBoxPreviewTool_Tests/Pods-SandBoxPreviewTool_Tests-frameworks.sh\"\n"; 321 | showEnvVarsInLog = 0; 322 | }; 323 | 43CA69166FDA28B599F01882 /* [CP] Check Pods Manifest.lock */ = { 324 | isa = PBXShellScriptBuildPhase; 325 | buildActionMask = 2147483647; 326 | files = ( 327 | ); 328 | inputFileListPaths = ( 329 | ); 330 | inputPaths = ( 331 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 332 | "${PODS_ROOT}/Manifest.lock", 333 | ); 334 | name = "[CP] Check Pods Manifest.lock"; 335 | outputFileListPaths = ( 336 | ); 337 | outputPaths = ( 338 | "$(DERIVED_FILE_DIR)/Pods-SandBoxPreviewTool_Example-checkManifestLockResult.txt", 339 | ); 340 | runOnlyForDeploymentPostprocessing = 0; 341 | shellPath = /bin/sh; 342 | 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"; 343 | showEnvVarsInLog = 0; 344 | }; 345 | B35AEFF6FE2C5929242B2453 /* [CP] Check Pods Manifest.lock */ = { 346 | isa = PBXShellScriptBuildPhase; 347 | buildActionMask = 2147483647; 348 | files = ( 349 | ); 350 | inputFileListPaths = ( 351 | ); 352 | inputPaths = ( 353 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 354 | "${PODS_ROOT}/Manifest.lock", 355 | ); 356 | name = "[CP] Check Pods Manifest.lock"; 357 | outputFileListPaths = ( 358 | ); 359 | outputPaths = ( 360 | "$(DERIVED_FILE_DIR)/Pods-SandBoxPreviewTool_Tests-checkManifestLockResult.txt", 361 | ); 362 | runOnlyForDeploymentPostprocessing = 0; 363 | shellPath = /bin/sh; 364 | 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"; 365 | showEnvVarsInLog = 0; 366 | }; 367 | F1C9D221B9B18870E0340BEB /* [CP] Embed Pods Frameworks */ = { 368 | isa = PBXShellScriptBuildPhase; 369 | buildActionMask = 2147483647; 370 | files = ( 371 | ); 372 | inputPaths = ( 373 | "${PODS_ROOT}/Target Support Files/Pods-SandBoxPreviewTool_Example/Pods-SandBoxPreviewTool_Example-frameworks.sh", 374 | "${BUILT_PRODUCTS_DIR}/SandBoxPreviewTool/SandBoxPreviewTool.framework", 375 | ); 376 | name = "[CP] Embed Pods Frameworks"; 377 | outputPaths = ( 378 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SandBoxPreviewTool.framework", 379 | ); 380 | runOnlyForDeploymentPostprocessing = 0; 381 | shellPath = /bin/sh; 382 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SandBoxPreviewTool_Example/Pods-SandBoxPreviewTool_Example-frameworks.sh\"\n"; 383 | showEnvVarsInLog = 0; 384 | }; 385 | /* End PBXShellScriptBuildPhase section */ 386 | 387 | /* Begin PBXSourcesBuildPhase section */ 388 | 6003F586195388D20070C39A /* Sources */ = { 389 | isa = PBXSourcesBuildPhase; 390 | buildActionMask = 2147483647; 391 | files = ( 392 | 6003F59E195388D20070C39A /* SPAppDelegate.m in Sources */, 393 | 6003F5A7195388D20070C39A /* SPViewController.m in Sources */, 394 | 6003F59A195388D20070C39A /* main.m in Sources */, 395 | ); 396 | runOnlyForDeploymentPostprocessing = 0; 397 | }; 398 | 6003F5AA195388D20070C39A /* Sources */ = { 399 | isa = PBXSourcesBuildPhase; 400 | buildActionMask = 2147483647; 401 | files = ( 402 | 6003F5BC195388D20070C39A /* Tests.m in Sources */, 403 | ); 404 | runOnlyForDeploymentPostprocessing = 0; 405 | }; 406 | /* End PBXSourcesBuildPhase section */ 407 | 408 | /* Begin PBXTargetDependency section */ 409 | 6003F5B4195388D20070C39A /* PBXTargetDependency */ = { 410 | isa = PBXTargetDependency; 411 | target = 6003F589195388D20070C39A /* SandBoxPreviewTool_Example */; 412 | targetProxy = 6003F5B3195388D20070C39A /* PBXContainerItemProxy */; 413 | }; 414 | /* End PBXTargetDependency section */ 415 | 416 | /* Begin PBXVariantGroup section */ 417 | 6003F596195388D20070C39A /* InfoPlist.strings */ = { 418 | isa = PBXVariantGroup; 419 | children = ( 420 | 6003F597195388D20070C39A /* en */, 421 | ); 422 | name = InfoPlist.strings; 423 | sourceTree = ""; 424 | }; 425 | 6003F5B8195388D20070C39A /* InfoPlist.strings */ = { 426 | isa = PBXVariantGroup; 427 | children = ( 428 | 6003F5B9195388D20070C39A /* en */, 429 | ); 430 | name = InfoPlist.strings; 431 | sourceTree = ""; 432 | }; 433 | 71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */ = { 434 | isa = PBXVariantGroup; 435 | children = ( 436 | 71719F9E1E33DC2100824A3D /* Base */, 437 | ); 438 | name = LaunchScreen.storyboard; 439 | sourceTree = ""; 440 | }; 441 | /* End PBXVariantGroup section */ 442 | 443 | /* Begin XCBuildConfiguration section */ 444 | 6003F5BD195388D20070C39A /* Debug */ = { 445 | isa = XCBuildConfiguration; 446 | buildSettings = { 447 | ALWAYS_SEARCH_USER_PATHS = NO; 448 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 449 | CLANG_CXX_LIBRARY = "libc++"; 450 | CLANG_ENABLE_MODULES = YES; 451 | CLANG_ENABLE_OBJC_ARC = YES; 452 | CLANG_WARN_BOOL_CONVERSION = YES; 453 | CLANG_WARN_CONSTANT_CONVERSION = YES; 454 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 455 | CLANG_WARN_EMPTY_BODY = YES; 456 | CLANG_WARN_ENUM_CONVERSION = YES; 457 | CLANG_WARN_INT_CONVERSION = YES; 458 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 459 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 460 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 461 | COPY_PHASE_STRIP = NO; 462 | ENABLE_TESTABILITY = YES; 463 | GCC_C_LANGUAGE_STANDARD = gnu99; 464 | GCC_DYNAMIC_NO_PIC = NO; 465 | GCC_OPTIMIZATION_LEVEL = 0; 466 | GCC_PREPROCESSOR_DEFINITIONS = ( 467 | "DEBUG=1", 468 | "$(inherited)", 469 | ); 470 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 471 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 472 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 473 | GCC_WARN_UNDECLARED_SELECTOR = YES; 474 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 475 | GCC_WARN_UNUSED_FUNCTION = YES; 476 | GCC_WARN_UNUSED_VARIABLE = YES; 477 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 478 | ONLY_ACTIVE_ARCH = YES; 479 | SDKROOT = iphoneos; 480 | TARGETED_DEVICE_FAMILY = "1,2"; 481 | }; 482 | name = Debug; 483 | }; 484 | 6003F5BE195388D20070C39A /* Release */ = { 485 | isa = XCBuildConfiguration; 486 | buildSettings = { 487 | ALWAYS_SEARCH_USER_PATHS = NO; 488 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 489 | CLANG_CXX_LIBRARY = "libc++"; 490 | CLANG_ENABLE_MODULES = YES; 491 | CLANG_ENABLE_OBJC_ARC = YES; 492 | CLANG_WARN_BOOL_CONVERSION = YES; 493 | CLANG_WARN_CONSTANT_CONVERSION = YES; 494 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 495 | CLANG_WARN_EMPTY_BODY = YES; 496 | CLANG_WARN_ENUM_CONVERSION = YES; 497 | CLANG_WARN_INT_CONVERSION = YES; 498 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 499 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 500 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 501 | COPY_PHASE_STRIP = YES; 502 | ENABLE_NS_ASSERTIONS = NO; 503 | GCC_C_LANGUAGE_STANDARD = gnu99; 504 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 505 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 506 | GCC_WARN_UNDECLARED_SELECTOR = YES; 507 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 508 | GCC_WARN_UNUSED_FUNCTION = YES; 509 | GCC_WARN_UNUSED_VARIABLE = YES; 510 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 511 | SDKROOT = iphoneos; 512 | TARGETED_DEVICE_FAMILY = "1,2"; 513 | VALIDATE_PRODUCT = YES; 514 | }; 515 | name = Release; 516 | }; 517 | 6003F5C0195388D20070C39A /* Debug */ = { 518 | isa = XCBuildConfiguration; 519 | baseConfigurationReference = 75071BA86B19BA1DE6B6628E /* Pods-SandBoxPreviewTool_Example.debug.xcconfig */; 520 | buildSettings = { 521 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 522 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 523 | GCC_PREFIX_HEADER = "SandBoxPreviewTool/SandBoxPreviewTool-Prefix.pch"; 524 | INFOPLIST_FILE = "SandBoxPreviewTool/SandBoxPreviewTool-Info.plist"; 525 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 526 | MODULE_NAME = ExampleApp; 527 | PRODUCT_BUNDLE_IDENTIFIER = com.SandBoxPreviewTool.demo; 528 | PRODUCT_NAME = "$(TARGET_NAME)"; 529 | SWIFT_VERSION = 4.0; 530 | TARGETED_DEVICE_FAMILY = "1,2"; 531 | WRAPPER_EXTENSION = app; 532 | }; 533 | name = Debug; 534 | }; 535 | 6003F5C1195388D20070C39A /* Release */ = { 536 | isa = XCBuildConfiguration; 537 | baseConfigurationReference = 0187677F179E6903755B51B3 /* Pods-SandBoxPreviewTool_Example.release.xcconfig */; 538 | buildSettings = { 539 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 540 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 541 | GCC_PREFIX_HEADER = "SandBoxPreviewTool/SandBoxPreviewTool-Prefix.pch"; 542 | INFOPLIST_FILE = "SandBoxPreviewTool/SandBoxPreviewTool-Info.plist"; 543 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 544 | MODULE_NAME = ExampleApp; 545 | PRODUCT_BUNDLE_IDENTIFIER = com.SandBoxPreviewTool.demo; 546 | PRODUCT_NAME = "$(TARGET_NAME)"; 547 | SWIFT_VERSION = 4.0; 548 | TARGETED_DEVICE_FAMILY = "1,2"; 549 | WRAPPER_EXTENSION = app; 550 | }; 551 | name = Release; 552 | }; 553 | 6003F5C3195388D20070C39A /* Debug */ = { 554 | isa = XCBuildConfiguration; 555 | baseConfigurationReference = AEE8C9032A20BCAB80AF04CB /* Pods-SandBoxPreviewTool_Tests.debug.xcconfig */; 556 | buildSettings = { 557 | BUNDLE_LOADER = "$(TEST_HOST)"; 558 | FRAMEWORK_SEARCH_PATHS = ( 559 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 560 | "$(inherited)", 561 | "$(DEVELOPER_FRAMEWORKS_DIR)", 562 | ); 563 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 564 | GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch"; 565 | GCC_PREPROCESSOR_DEFINITIONS = ( 566 | "DEBUG=1", 567 | "$(inherited)", 568 | ); 569 | INFOPLIST_FILE = "Tests/Tests-Info.plist"; 570 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; 571 | PRODUCT_NAME = "$(TARGET_NAME)"; 572 | SWIFT_VERSION = 4.0; 573 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SandBoxPreviewTool_Example.app/SandBoxPreviewTool_Example"; 574 | WRAPPER_EXTENSION = xctest; 575 | }; 576 | name = Debug; 577 | }; 578 | 6003F5C4195388D20070C39A /* Release */ = { 579 | isa = XCBuildConfiguration; 580 | baseConfigurationReference = 4DA473B2CB89C8A392E56D58 /* Pods-SandBoxPreviewTool_Tests.release.xcconfig */; 581 | buildSettings = { 582 | BUNDLE_LOADER = "$(TEST_HOST)"; 583 | FRAMEWORK_SEARCH_PATHS = ( 584 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 585 | "$(inherited)", 586 | "$(DEVELOPER_FRAMEWORKS_DIR)", 587 | ); 588 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 589 | GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch"; 590 | INFOPLIST_FILE = "Tests/Tests-Info.plist"; 591 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; 592 | PRODUCT_NAME = "$(TARGET_NAME)"; 593 | SWIFT_VERSION = 4.0; 594 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SandBoxPreviewTool_Example.app/SandBoxPreviewTool_Example"; 595 | WRAPPER_EXTENSION = xctest; 596 | }; 597 | name = Release; 598 | }; 599 | /* End XCBuildConfiguration section */ 600 | 601 | /* Begin XCConfigurationList section */ 602 | 6003F585195388D10070C39A /* Build configuration list for PBXProject "SandBoxPreviewTool" */ = { 603 | isa = XCConfigurationList; 604 | buildConfigurations = ( 605 | 6003F5BD195388D20070C39A /* Debug */, 606 | 6003F5BE195388D20070C39A /* Release */, 607 | ); 608 | defaultConfigurationIsVisible = 0; 609 | defaultConfigurationName = Release; 610 | }; 611 | 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "SandBoxPreviewTool_Example" */ = { 612 | isa = XCConfigurationList; 613 | buildConfigurations = ( 614 | 6003F5C0195388D20070C39A /* Debug */, 615 | 6003F5C1195388D20070C39A /* Release */, 616 | ); 617 | defaultConfigurationIsVisible = 0; 618 | defaultConfigurationName = Release; 619 | }; 620 | 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "SandBoxPreviewTool_Tests" */ = { 621 | isa = XCConfigurationList; 622 | buildConfigurations = ( 623 | 6003F5C3195388D20070C39A /* Debug */, 624 | 6003F5C4195388D20070C39A /* Release */, 625 | ); 626 | defaultConfigurationIsVisible = 0; 627 | defaultConfigurationName = Release; 628 | }; 629 | /* End XCConfigurationList section */ 630 | }; 631 | rootObject = 6003F582195388D10070C39A /* Project object */; 632 | } 633 | -------------------------------------------------------------------------------- /Example/SandBoxPreviewTool.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/SandBoxPreviewTool.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/SandBoxPreviewTool/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 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Example/SandBoxPreviewTool/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 | -------------------------------------------------------------------------------- /Example/SandBoxPreviewTool/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Example/SandBoxPreviewTool/SPAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // SPAppDelegate.h 3 | // SandBoxPreviewTool 4 | // 5 | 6 | @import UIKit; 7 | 8 | @interface SPAppDelegate : UIResponder 9 | 10 | @property (strong, nonatomic) UIWindow *window; 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /Example/SandBoxPreviewTool/SPAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // SPAppDelegate.m 3 | // SandBoxPreviewTool 4 | // 5 | 6 | #import "SPAppDelegate.h" 7 | 8 | @implementation SPAppDelegate 9 | 10 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 11 | { 12 | // Override point for customization after application launch. 13 | return YES; 14 | } 15 | 16 | - (void)applicationWillResignActive:(UIApplication *)application 17 | { 18 | // 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. 19 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 20 | } 21 | 22 | - (void)applicationDidEnterBackground:(UIApplication *)application 23 | { 24 | // 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. 25 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 26 | } 27 | 28 | - (void)applicationWillEnterForeground:(UIApplication *)application 29 | { 30 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 31 | } 32 | 33 | - (void)applicationDidBecomeActive:(UIApplication *)application 34 | { 35 | // 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. 36 | } 37 | 38 | - (void)applicationWillTerminate:(UIApplication *)application 39 | { 40 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /Example/SandBoxPreviewTool/SPViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SPViewController.h 3 | // SandBoxPreviewTool 4 | // 5 | 6 | @import UIKit; 7 | 8 | @interface SPViewController : UIViewController 9 | 10 | @end 11 | -------------------------------------------------------------------------------- /Example/SandBoxPreviewTool/SPViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SPViewController.m 3 | // SandBoxPreviewTool 4 | // 5 | 6 | #import "SPViewController.h" 7 | #import 8 | #import //悬浮球按钮 9 | 10 | @interface SPViewController () 11 | 12 | @end 13 | 14 | @implementation SPViewController 15 | 16 | - (void)viewDidLoad 17 | { 18 | [super viewDidLoad]; 19 | self.view.backgroundColor = UIColor.darkGrayColor; 20 | [self createDebugSuspensionButton]; 21 | } 22 | 23 | // 创建悬浮球按钮 24 | - (void)createDebugSuspensionButton{ 25 | //自行添加哦~ 记得上线前要去除哦。 QA或者调试开发阶段可以这么使用 26 | SuspensionButton * button = [[SuspensionButton alloc] initWithFrame:CGRectMake(-5, [UIScreen mainScreen].bounds.size.height/2 - 100 , 50, 50) color:[UIColor colorWithRed:135/255.0 green:216/255.0 blue:80/255.0 alpha:1]]; 27 | button.leanType = SuspensionViewLeanTypeEachSide; 28 | [button addTarget:self action:@selector(pushToDebugPage) forControlEvents:UIControlEventTouchUpInside]; 29 | [self.view addSubview:button]; 30 | } 31 | 32 | //open or close sandbox preview 33 | - (void)pushToDebugPage{ 34 | [[SandBoxPreviewTool sharedTool] autoOpenCloseApplicationDiskDirectoryPanel]; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /Example/SandBoxPreviewTool/SandBoxPreviewTool-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | UIInterfaceOrientationLandscapeRight 40 | 41 | UISupportedInterfaceOrientations~ipad 42 | 43 | UIInterfaceOrientationPortrait 44 | UIInterfaceOrientationPortraitUpsideDown 45 | UIInterfaceOrientationLandscapeLeft 46 | UIInterfaceOrientationLandscapeRight 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Example/SandBoxPreviewTool/SandBoxPreviewTool-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_5_0 10 | #warning "This project uses features only available in iOS SDK 5.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | @import UIKit; 15 | @import Foundation; 16 | #endif 17 | -------------------------------------------------------------------------------- /Example/SandBoxPreviewTool/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Example/SandBoxPreviewTool/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // SandBoxPreviewTool 4 | // 5 | @import UIKit; 6 | #import "SPAppDelegate.h" 7 | 8 | int main(int argc, char * argv[]) 9 | { 10 | @autoreleasepool { 11 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([SPAppDelegate class])); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Example/Tests/Tests-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 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Example/Tests/Tests-Prefix.pch: -------------------------------------------------------------------------------- 1 | // The contents of this file are implicitly included at the beginning of every test case source file. 2 | 3 | #ifdef __OBJC__ 4 | 5 | @import Specta; 6 | @import Expecta; 7 | @import FBSnapshotTestCase; 8 | @import Expecta_Snapshots; 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /Example/Tests/Tests.m: -------------------------------------------------------------------------------- 1 | // 2 | // SandBoxPreviewToolTests.m 3 | // SandBoxPreviewToolTests 4 | // 5 | // Created by 492838605@qq.com on 11/11/2020. 6 | // Copyright (c) 2020 492838605@qq.com. All rights reserved. 7 | // 8 | 9 | // https://github.com/Specta/Specta 10 | 11 | SpecBegin(InitialSpecs) 12 | 13 | describe(@"these will fail", ^{ 14 | 15 | it(@"can do maths", ^{ 16 | expect(1).to.equal(2); 17 | }); 18 | 19 | it(@"can read", ^{ 20 | expect(@"number").to.equal(@"string"); 21 | }); 22 | 23 | it(@"will wait for 10 seconds and fail", ^{ 24 | waitUntil(^(DoneCallback done) { 25 | 26 | }); 27 | }); 28 | }); 29 | 30 | describe(@"these will pass", ^{ 31 | 32 | it(@"can do maths", ^{ 33 | expect(1).beLessThan(23); 34 | }); 35 | 36 | it(@"can read", ^{ 37 | expect(@"team").toNot.contain(@"I"); 38 | }); 39 | 40 | it(@"will wait and succeed", ^{ 41 | waitUntil(^(DoneCallback done) { 42 | done(); 43 | }); 44 | }); 45 | }); 46 | 47 | SpecEnd 48 | 49 | -------------------------------------------------------------------------------- /Example/Tests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 492838605@qq.com 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SandBoxPreviewTool 2 | 3 | ### 一两行代码就能轻松查看ios应用沙盒文件。debug好帮手 4 | 5 | 很多项目中,都或多或少有文件下载,db存储或其他类型文件缓存/音视频图片缓存等等。 6 | 7 | 可是,真机或者调试阶段要查看这些文件就需要各位开发同学进入对应沙盒路径文件夹自行查看,非常不方便。对于真机,更是麻烦。 8 | 9 | 建议各位同发同学都可以在项目提测阶段,添加悬浮球,一键跳转沙盒,方便试试查看缓存文件信息或者分享文件到PC。 10 | 11 | json,plist,html,css,log日志,图片等支持应用内查看。 12 | 13 | sqlite,realm等文件(会自动忽略部分管道文件),支持AirDrop分享查看。 14 | 15 | 附带文件MD5信息,适合文件下载后查看本地文件信息,校验文件完整性。 16 | 17 | 例如:如果你觉得自己应用中的数据库文件写的有问题,可以直接将对应的db或realm文件通过AirDrop分享到电脑后,然后通过相关应用直接输入sql语句进行debug。 18 | 19 | ``` 20 | #import //悬浮球按钮 21 | 22 | //按钮点击事件中调用 23 | - (IBAction)click:(id)sender { 24 | //[[SandBoxPreviewTool sharedTool] setOpenLog:YES];是否开启控制台打印文件路径。不用可自行忽略 25 | [[SandBoxPreviewTool sharedTool] autoOpenCloseApplicationDiskDirectoryPanel]; 26 | } 27 | ``` 28 | 29 | 赠送附加功能1:查看文件MD5值,建议有重要文件下载、上传都严格校验文件MD5摘要,防止文件网络请求过程出错或被篡改等等意外。 30 | ``` 31 | #import 32 | 33 | //xx_filePath 文件沙盒路径(不能设置为文件夹路径) 34 | [LJ_FileInfo getFileMD5WithPath: xx_filePath]; 35 | 36 | ``` 37 | 赠送附加功能2:悬浮球 38 | ```在AppDelegate.m中导入 39 | #import //悬浮球按钮 40 | 41 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ 42 |    ...创建window以后向window添加悬浮球 43 |    [self createDebugSuspensionButton]; 44 | 45 | } 46 | 47 | // 创建悬浮球按钮 48 | - (void)createDebugSuspensionButton{ 49 |   //自行添加哦~ 记得上线前要去除哦。 QA或者调试开发阶段可以这么使用 50 |  SuspensionButton * button = [[SuspensionButton alloc] initWithFrame:CGRectMake(-5, [UIScreen mainScreen].bounds.size.height/2 - 100 , 50, 50) color:[UIColor colorWithRed:135/255.0 green:216/255.0 blue:80/255.0 alpha:1]]; 51 | button.leanType = SuspensionViewLeanTypeEachSide; 52 | [button addTarget:self action:@selector(pushToDebugPage) forControlEvents:UIControlEventTouchUpInside]; 53 | [self.window.rootViewController.view addSubview:button]; 54 | } 55 | 56 | //open or close sandbox preview 57 | - (void)pushToDebugPage{ 58 | [[SandBoxPreviewTool sharedTool] autoOpenCloseApplicationDiskDirectoryPanel]; 59 | } 60 | 61 | ``` 62 | #### 安装 63 | ``` 64 | pod "SandBoxPreviewTool" 65 | ``` 66 | 67 | #### 部分样例 68 | 69 | 初次打开的样子,测试按钮自行忽略 70 |
71 | 72 | 73 | 通过AirDrop或三方分享 共享文件 74 |
75 | 76 | 77 | 查看某个文件下所有子文件 可以方便得查看类似于SDWebImage工具的图片缓存 78 |
79 | 80 | 81 | 查看沙盒中下载的文件,MD5值可以用于校验文件是否下载出错。 82 |
83 | 84 | 85 | 86 | #### 其他建议 87 | 其实开发中大家一样可以使用在info.plist中添加UIFileSharingEnabled为true。 88 | 这样可以很容易的itunes中查看Documents文件目录下文件内容。 89 | 但是如果没有特殊需要,上线前一定要关闭该功能哦~ 90 | -------------------------------------------------------------------------------- /SandBoxPreviewTool.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint SandBoxPreviewTool.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = 'SandBoxPreviewTool' 11 | s.version = '1.2.2' 12 | s.summary = 'Make iOS preview sandbox in your app more easy' 13 | s.description = <<-DESC 14 | help you preview sandbox easy in you application with one line of code 15 | DESC 16 | s.homepage = 'https://github.com/wjyx1lalala/SandBoxPreviewTool' 17 | s.license = { :type => 'MIT', :file => 'LICENSE' } 18 | s.author = { "nuomi" => "492838605@qq.com" } 19 | s.source = { :git => "https://github.com/wjyx1lalala/SandBoxPreviewTool.git", :tag => s.version } 20 | s.ios.deployment_target = '9.0' 21 | s.source_files = 'SandBoxPreviewTool/Classes/*' 22 | s.resource = 'SandBoxPreviewTool/Assets/*.bundle' 23 | s.frameworks = 'UIKit' 24 | 25 | # s.resource_bundles = { 26 | # 'SandBoxPreviewTool' => ['SandBoxPreviewTool/Assets/*.png'] 27 | # } 28 | # s.public_header_files = 'Pod/Classes/*.h' 29 | 30 | end 31 | -------------------------------------------------------------------------------- /SandBoxPreviewTool/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjyx1lalala/SandBoxPreviewTool/c382953af5da987fec2d8b530a227e0a37789757/SandBoxPreviewTool/Assets/.gitkeep -------------------------------------------------------------------------------- /SandBoxPreviewTool/Assets/SandBoxPreviewTool.bundle/GenericFolderIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjyx1lalala/SandBoxPreviewTool/c382953af5da987fec2d8b530a227e0a37789757/SandBoxPreviewTool/Assets/SandBoxPreviewTool.bundle/GenericFolderIcon.png -------------------------------------------------------------------------------- /SandBoxPreviewTool/Assets/SandBoxPreviewTool.bundle/back_icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjyx1lalala/SandBoxPreviewTool/c382953af5da987fec2d8b530a227e0a37789757/SandBoxPreviewTool/Assets/SandBoxPreviewTool.bundle/back_icon@2x.png -------------------------------------------------------------------------------- /SandBoxPreviewTool/Assets/SandBoxPreviewTool.bundle/back_icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjyx1lalala/SandBoxPreviewTool/c382953af5da987fec2d8b530a227e0a37789757/SandBoxPreviewTool/Assets/SandBoxPreviewTool.bundle/back_icon@3x.png -------------------------------------------------------------------------------- /SandBoxPreviewTool/Assets/SandBoxPreviewTool.bundle/lj_Pictures.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjyx1lalala/SandBoxPreviewTool/c382953af5da987fec2d8b530a227e0a37789757/SandBoxPreviewTool/Assets/SandBoxPreviewTool.bundle/lj_Pictures.png -------------------------------------------------------------------------------- /SandBoxPreviewTool/Assets/SandBoxPreviewTool.bundle/lj_unknow_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjyx1lalala/SandBoxPreviewTool/c382953af5da987fec2d8b530a227e0a37789757/SandBoxPreviewTool/Assets/SandBoxPreviewTool.bundle/lj_unknow_icon.png -------------------------------------------------------------------------------- /SandBoxPreviewTool/Classes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wjyx1lalala/SandBoxPreviewTool/c382953af5da987fec2d8b530a227e0a37789757/SandBoxPreviewTool/Classes/.gitkeep -------------------------------------------------------------------------------- /SandBoxPreviewTool/Classes/LJAlertView.h: -------------------------------------------------------------------------------- 1 | // 2 | // LJAlertView.h 3 | // LJHotUpdate 4 | // 5 | // Created by nuomi on 2017/2/9. 6 | // Copyright © 2017年 xgyg. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LJAlertView : UIView 12 | 13 | @property (nonatomic,strong)NSArray * dataSource; 14 | 15 | @property (nonatomic,copy)NSString * cancleTitle; 16 | 17 | /** 18 | * Alert提示框 19 | * 20 | * @param dataSource 提示框显示的内容 21 | * @param cancleTitle 取消键名称,nil则默认为@"取消" 22 | * @param clickBlock 点击后的回调 23 | * 24 | */ 25 | + (LJAlertView *)createAlertViewWith:(NSArray * )dataSource AndCancleTitle:(NSString *)cancleTitle andClick:(void(^)(int row))clickBlock; 26 | 27 | /** 28 | * 弹出提示框 29 | */ 30 | - (void)show; 31 | 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /SandBoxPreviewTool/Classes/LJAlertView.m: -------------------------------------------------------------------------------- 1 | // 2 | // LJAlertView.m 3 | // LJHotUpdate 4 | // 5 | // Created by nuomi on 2017/2/9. 6 | // Copyright © 2017年 xgyg. All rights reserved. 7 | // 8 | 9 | #import "LJAlertView.h" 10 | 11 | #define HeightProportion [UIScreen mainScreen].bounds.size.height / 575.0f 12 | #define DefaultGroupGap 6.0f//间隔大小 13 | #define DefaultCellHeight 45 14 | #define AnimationShowDuration .4f//弹出动画时长 15 | #define AnimationHiddenDuration .2f//隐藏动画时长 16 | #define kFontSize 16.0f //字体大小 17 | 18 | static NSString * const TopCellID = @"top"; 19 | static NSString * const BottomCellID = @"bottom"; 20 | 21 | @interface LJAlertView () 22 | 23 | @property (nonatomic,strong)UITableView * tableView; 24 | @property (nonatomic,copy) void(^clickBlock)(int row); 25 | @property (nonatomic,assign)BOOL isShow; 26 | @end 27 | 28 | @implementation LJAlertView 29 | 30 | + (LJAlertView *)createAlertViewWith:(NSArray * )dataSource AndCancleTitle:(NSString *)cancleTitle andClick:(void(^)(int row))clickBlock{ 31 | LJAlertView * alertView = [[LJAlertView alloc]initWithFrame:[UIScreen mainScreen].bounds]; 32 | alertView.dataSource = dataSource; 33 | alertView.cancleTitle = cancleTitle; 34 | alertView.clickBlock = clickBlock; 35 | [alertView.tableView reloadData]; 36 | return alertView; 37 | } 38 | 39 | - (instancetype)initWithFrame:(CGRect)frame 40 | { 41 | self = [super initWithFrame:frame]; 42 | if (self) { 43 | self.backgroundColor = [UIColor clearColor]; 44 | UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(hiddenWithDiss)]; 45 | tap.delegate = self; 46 | [self addGestureRecognizer:tap]; 47 | } 48 | return self; 49 | } 50 | 51 | #pragma mark - 防止手势冲突 52 | -(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch { 53 | if ([NSStringFromClass([touch.view class]) isEqualToString:NSStringFromClass([self class])]) { 54 | return YES; 55 | }else{ 56 | return NO; 57 | } 58 | } 59 | 60 | //隐藏 61 | - (void)hiddenWithDiss{ 62 | CGRect frame = self.tableView.frame; 63 | 64 | CGFloat height = DefaultCellHeight * (self.dataSource.count + 1) + DefaultGroupGap; 65 | frame.origin.y += height; 66 | _isShow = NO; 67 | UIColor * color = [UIColor clearColor]; 68 | [UIView animateWithDuration:AnimationHiddenDuration animations:^{ 69 | self.backgroundColor = color; 70 | self.tableView.frame = frame; 71 | } completion:^(BOOL finished) { 72 | [self removeFromSuperview]; 73 | }]; 74 | } 75 | 76 | //弹出 77 | - (void)show{ 78 | 79 | if (self.isShow) { 80 | [self hiddenWithDiss]; 81 | return ; 82 | } 83 | 84 | [self bringSubviewToFront:self.tableView]; 85 | [[UIApplication sharedApplication].keyWindow addSubview:self]; 86 | 87 | CGRect frame = self.tableView.frame; 88 | CGFloat height = DefaultCellHeight * (self.dataSource.count + 1) + DefaultGroupGap; 89 | frame.origin.y -= height; 90 | 91 | UIColor * viewColor = [UIColor blackColor]; 92 | viewColor = [viewColor colorWithAlphaComponent:.3f]; 93 | _isShow = YES; 94 | [UIView animateWithDuration:AnimationShowDuration delay:0 usingSpringWithDamping:0.7 initialSpringVelocity:0.5 options:UIViewAnimationOptionAllowUserInteraction animations:^{ 95 | 96 | self.tableView.frame = frame; 97 | 98 | self.backgroundColor = viewColor; 99 | 100 | } completion:^(BOOL finished) { 101 | 102 | }]; 103 | } 104 | 105 | - (UITableView *)tableView{ 106 | if (_tableView == nil) { 107 | float footerHeight = 20; 108 | _tableView = [[UITableView alloc]initWithFrame:CGRectMake(0, [UIScreen mainScreen].bounds.size.height, [UIScreen mainScreen].bounds.size.width, DefaultCellHeight * (self.dataSource.count + 1) + DefaultGroupGap + footerHeight)]; 109 | _tableView.bounces = NO; 110 | _tableView.delegate = self; 111 | _tableView.dataSource = self; 112 | UIColor * color = [UIColor colorWithRed:0.84 green:0.84 blue:0.85 alpha:1]; 113 | _tableView.backgroundColor = color; 114 | _tableView.rowHeight = DefaultCellHeight; 115 | _tableView.separatorColor = color; 116 | [self addSubview:_tableView]; 117 | //for spring animation 118 | UIView * footerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, _tableView.frame.size.width, footerHeight)]; 119 | footerView.backgroundColor = [UIColor whiteColor]; 120 | _tableView.tableFooterView = footerView; 121 | } 122 | return _tableView; 123 | } 124 | 125 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ 126 | 127 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 128 | if (indexPath.section != 1) { 129 | if (self.clickBlock) { 130 | self.clickBlock((int)indexPath.row); 131 | } 132 | } 133 | [self hiddenWithDiss]; 134 | } 135 | 136 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ 137 | 138 | return 2; 139 | 140 | } 141 | 142 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ 143 | 144 | return section ? 1:self.dataSource.count; 145 | 146 | } 147 | 148 | - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{ 149 | 150 | return section ? DefaultGroupGap: 0; 151 | 152 | } 153 | 154 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ 155 | NSString * cellid = nil; 156 | if (indexPath.section == 0) { 157 | cellid = TopCellID; 158 | }else{ 159 | cellid = BottomCellID; 160 | } 161 | UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:cellid]; 162 | if (cell == nil) { 163 | cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellid]; 164 | } 165 | UILabel * label = [[UILabel alloc]initWithFrame:CGRectZero]; 166 | label.textAlignment = NSTextAlignmentCenter; 167 | label.center = cell.contentView.center; 168 | label.textColor = [UIColor colorWithRed:0.12 green:0.12 blue:0.12 alpha:1]; 169 | label.font = [UIFont systemFontOfSize:kFontSize]; 170 | [cell.contentView addSubview:label]; 171 | //居中 172 | label.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, DefaultCellHeight); 173 | if (indexPath.section == 0) { 174 | label.text = [self.dataSource objectAtIndex:indexPath.row]; 175 | }else{ 176 | if (self.cancleTitle && [self.cancleTitle isKindOfClass:[NSString class]] && self.cancleTitle.length) { 177 | label.text = self.cancleTitle; 178 | }else{ 179 | label.text = @"取消"; 180 | } 181 | } 182 | [cell setSeparatorInset:UIEdgeInsetsZero]; 183 | [cell setLayoutMargins:UIEdgeInsetsZero]; 184 | [cell setPreservesSuperviewLayoutMargins:NO]; 185 | return cell; 186 | } 187 | 188 | 189 | @end 190 | -------------------------------------------------------------------------------- /SandBoxPreviewTool/Classes/LJ_DirToolNavigatorController.h: -------------------------------------------------------------------------------- 1 | // 2 | // LJ_DirToolNavigatorController.h 3 | // FileDirectoryTool 4 | // 5 | // Created by nuomi on 2017/2/7. 6 | // Copyright © 2017年 xgyg. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LJ_DirToolNavigatorController : UINavigationController 12 | 13 | + (instancetype)create; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /SandBoxPreviewTool/Classes/LJ_DirToolNavigatorController.m: -------------------------------------------------------------------------------- 1 | // 2 | // LJ_DirToolNavigatorController.m 3 | // FileDirectoryTool 4 | // 5 | // Created by nuomi on 2017/2/7. 6 | // Copyright © 2017年 xgyg. All rights reserved. 7 | // 8 | 9 | #import "LJ_DirToolNavigatorController.h" 10 | #import "LJ_HomeDirViewController.h" 11 | #import "SuspensionButton.h" 12 | #import "SLUnilityObject.h" 13 | @interface LJ_DirToolNavigatorController () 14 | 15 | @end 16 | 17 | 18 | @implementation LJ_DirToolNavigatorController 19 | 20 | 21 | + (instancetype)create{ 22 | LJ_HomeDirViewController * homeDir = [[LJ_HomeDirViewController alloc] init]; 23 | homeDir.isHomeDir = YES; 24 | return [[LJ_DirToolNavigatorController alloc] initWithRootViewController:homeDir]; 25 | } 26 | 27 | - (void)viewDidLoad { 28 | [super viewDidLoad]; 29 | 30 | UINavigationBar *navBar = self.navigationBar; 31 | navBar.titleTextAttributes = @{NSForegroundColorAttributeName: [UIColor colorWithRed:0.356 green:0.356 blue:0.356 alpha:1], NSFontAttributeName : [UIFont systemFontOfSize:17]}; 32 | navBar.tintColor = [UIColor colorWithRed:0.356 green:0.356 blue:0.356 alpha:1]; 33 | navBar.barTintColor = [UIColor whiteColor]; 34 | 35 | SuspensionButton * button = [[SuspensionButton alloc] initWithFrame:CGRectMake(-5, [UIScreen mainScreen].bounds.size.height/2 , 50, 50) color:[UIColor colorWithRed:135/255.0 green:216/255.0 blue:80/255.0 alpha:1]]; 36 | button.leanType = SuspensionViewLeanTypeEachSide; 37 | [button addTarget:self action:@selector(dismiss) forControlEvents:UIControlEventTouchUpInside]; 38 | [self.view addSubview:button]; 39 | } 40 | 41 | - (void)dismiss{ 42 | [self dismissViewControllerAnimated:YES completion:nil]; 43 | } 44 | 45 | 46 | - (instancetype)initWithRootViewController:(UIViewController *)rootViewController{ 47 | 48 | self = [super initWithRootViewController:rootViewController]; 49 | if (self) { 50 | self.interactivePopGestureRecognizer.delegate = self; 51 | } 52 | 53 | return self; 54 | } 55 | 56 | - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{ 57 | 58 | if (self.viewControllers.count > 0) { 59 | viewController.hidesBottomBarWhenPushed = YES; 60 | UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; 61 | // [button setBackgroundImage:[UIImage imageNamed:@"back_icon"] forState:UIControlStateNormal]; 62 | [button setBackgroundImage:[SLUnilityObject imageWithName:@"back_icon"] forState:UIControlStateNormal]; 63 | button.frame = (CGRect){CGPointZero, button.currentBackgroundImage.size}; 64 | [button addTarget:self action:@selector(back) forControlEvents:UIControlEventTouchUpInside]; 65 | viewController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:button]; 66 | } 67 | [super pushViewController:viewController animated:animated]; 68 | } 69 | 70 | 71 | - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{ 72 | return self.viewControllers.count == 1 ? NO : YES; 73 | } 74 | 75 | - (void)back{ 76 | [self popViewControllerAnimated:YES]; 77 | } 78 | 79 | 80 | - (void)didReceiveMemoryWarning { 81 | [super didReceiveMemoryWarning]; 82 | // Dispose of any resources that can be recreated. 83 | } 84 | 85 | @end 86 | -------------------------------------------------------------------------------- /SandBoxPreviewTool/Classes/LJ_FileInfo.h: -------------------------------------------------------------------------------- 1 | // 2 | // LJ_FileInfo.h 3 | // FileDirectoryTool 4 | // 5 | // Created by nuomi on 2017/2/7. 6 | // Copyright © 2017年 xgyg. All rights reserved. 7 | // 获取文件MD5 用于验证文件完整性 8 | 9 | #import 10 | 11 | @interface LJ_FileInfo : NSObject 12 | 13 | //获取制定路径文件的md5值,不能为文件夹,文件路径不能为空 14 | + (NSString*)getFileMD5WithPath:(NSString*)path; 15 | 16 | //查找指定路径下文件信息 17 | + (NSMutableArray*)searchAllFileFromRightDirPath:(NSString *)rightDirPath; 18 | 19 | //获取文件类型 20 | + (NSString *)judgeFileTypeWithPath:(NSString *)filePath; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /SandBoxPreviewTool/Classes/LJ_FileInfo.m: -------------------------------------------------------------------------------- 1 | // 2 | // LJ_FileInfo.m 3 | // FileDirectoryTool 4 | // 5 | // Created by nuomi on 2017/2/7. 6 | // Copyright © 2017年 xgyg. All rights reserved. 7 | // 8 | 9 | #import "LJ_FileInfo.h" 10 | #include 11 | 12 | #define FileHashDefaultChunkSizeForReadingData 1024*8 13 | 14 | @implementation LJ_FileInfo 15 | 16 | 17 | CFStringRef FileMD5HashCreateWithPath(CFStringRef filePath,size_t chunkSizeForReadingData) { 18 | // Declare needed variables 19 | CFStringRef result = NULL; 20 | CFReadStreamRef readStream = NULL; 21 | // Get the file URL 22 | CFURLRef fileURL = 23 | CFURLCreateWithFileSystemPath(kCFAllocatorDefault, 24 | (CFStringRef)filePath, 25 | kCFURLPOSIXPathStyle, 26 | (Boolean)false); 27 | if (!fileURL) goto done; 28 | // Create and open the read stream 29 | readStream = CFReadStreamCreateWithFile(kCFAllocatorDefault, 30 | (CFURLRef)fileURL); 31 | if (!readStream) goto done; 32 | bool didSucceed = (bool)CFReadStreamOpen(readStream); 33 | if (!didSucceed) goto done; 34 | // Initialize the hash object 35 | CC_MD5_CTX hashObject; 36 | CC_MD5_Init(&hashObject); 37 | // Make sure chunkSizeForReadingData is valid 38 | if (!chunkSizeForReadingData) { 39 | chunkSizeForReadingData = FileHashDefaultChunkSizeForReadingData; 40 | } 41 | // Feed the data to the hash object 42 | bool hasMoreData = true; 43 | while (hasMoreData) { 44 | uint8_t buffer[chunkSizeForReadingData]; 45 | CFIndex readBytesCount = CFReadStreamRead(readStream,(UInt8 *)buffer,(CFIndex)sizeof(buffer)); 46 | if (readBytesCount == -1) break; 47 | if (readBytesCount == 0) { 48 | hasMoreData = false; 49 | continue; 50 | } 51 | CC_MD5_Update(&hashObject,(const void *)buffer,(CC_LONG)readBytesCount); 52 | } 53 | // Check if the read operation succeeded 54 | didSucceed = !hasMoreData; 55 | // Compute the hash digest 56 | unsigned char digest[CC_MD5_DIGEST_LENGTH]; 57 | CC_MD5_Final(digest, &hashObject); 58 | // Abort if the read operation failed 59 | if (!didSucceed) goto done; 60 | // Compute the string result 61 | char hash[2 * sizeof(digest) + 1]; 62 | for (size_t i = 0; i < sizeof(digest); ++i) { 63 | snprintf(hash + (2 * i), 3, "%02x", (int)(digest[i])); 64 | } 65 | result = CFStringCreateWithCString(kCFAllocatorDefault,(const char *)hash,kCFStringEncodingUTF8); 66 | 67 | done: 68 | if (readStream) { 69 | CFReadStreamClose(readStream); 70 | CFRelease(readStream); 71 | } 72 | if (fileURL) { 73 | CFRelease(fileURL); 74 | } 75 | return result; 76 | } 77 | 78 | + (NSString*)getFileMD5WithPath:(NSString*)path 79 | { 80 | if (!path) { 81 | return @""; 82 | } 83 | return (__bridge_transfer NSString *)FileMD5HashCreateWithPath((__bridge CFStringRef)path, FileHashDefaultChunkSizeForReadingData); 84 | } 85 | 86 | + (NSMutableArray*)searchAllFileFromRightDirPath:(NSString *)rightDirPath{ 87 | NSFileManager * fileManager = [NSFileManager defaultManager]; 88 | BOOL isDir = NO; 89 | BOOL isExists = [fileManager fileExistsAtPath:rightDirPath isDirectory:&isDir]; 90 | if (!isExists || !isDir) { 91 | return nil; 92 | } 93 | 94 | NSMutableArray * subFileNamesArr = [NSMutableArray array]; 95 | 96 | NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtPath:rightDirPath]; 97 | for (NSString *fileName in enumerator){ 98 | if (fileName && [fileName hasPrefix:@"."] == NO && [fileName containsString:@"/"] == NO) { 99 | /* 100 | NSFileCreationDate = "2017-02-09 09:04:50 +0000"; 101 | NSFileExtensionHidden = 0; 102 | NSFileGroupOwnerAccountID = 20; 103 | NSFileGroupOwnerAccountName = staff; 104 | NSFileModificationDate = "2017-02-09 09:04:50 +0000"; 105 | NSFileOwnerAccountID = 501; 106 | NSFilePosixPermissions = 493; 107 | NSFileReferenceCount = 2; 108 | NSFileSize = 68;//文件大小 109 | NSFileSystemFileNumber = 13723856; 110 | NSFileSystemNumber = 16777218; 111 | NSFileType = NSFileTypeDirectory; 112 | canDel = 1; 113 | title = Documents;*/ 114 | 115 | NSFileManager * fileManager = [NSFileManager defaultManager]; 116 | //获取文件属性 117 | NSDictionary *fileAttributes = [fileManager attributesOfItemAtPath:[rightDirPath stringByAppendingPathComponent:fileName] error:nil]; 118 | NSMutableDictionary * dict = [NSMutableDictionary dictionaryWithDictionary:fileAttributes]; 119 | [dict setObject:fileName forKey:@"title"]; 120 | [dict setObject:[NSNumber numberWithBool:[[NSFileManager defaultManager] isDeletableFileAtPath:[NSHomeDirectory() stringByAppendingPathComponent:fileName]]] forKey:@"canDel"]; 121 | if ([[dict objectForKey:NSFileType] isEqualToString:NSFileTypeDirectory]) { 122 | [dict setObject:@"Dir" forKey:@"FileType"]; 123 | }else{ 124 | [dict setObject:[self judgeFileTypeWithPath:[rightDirPath stringByAppendingPathComponent:fileName]] forKey:@"FileType"]; 125 | } 126 | [subFileNamesArr addObject:dict]; 127 | } 128 | } 129 | return subFileNamesArr; 130 | } 131 | 132 | + (NSString *)judgeFileTypeWithPath:(NSString *)filePath{ 133 | if ([filePath hasSuffix:@".note"]) { 134 | return @"未知文件类型"; 135 | } 136 | NSData * data = [NSData dataWithContentsOfFile:filePath]; 137 | if (data.length<2) { 138 | return @"未知文件类型"; 139 | } 140 | int char1 = 0 ,char2 =0 ; 141 | [data getBytes:&char1 range:NSMakeRange(0, 1)]; 142 | [data getBytes:&char2 range:NSMakeRange(1, 1)]; 143 | data = nil; 144 | NSString *numStr = [NSString stringWithFormat:@"%i%i",char1,char2]; 145 | 146 | if ([numStr isEqualToString:@"255216"]) { 147 | return @"image/jpeg"; 148 | }else if ([numStr isEqualToString:@"7173"]) { 149 | return @"image/gif"; 150 | }else if ([numStr isEqualToString:@"6677"]) { 151 | return @"image/bmp"; 152 | }else if ([numStr isEqualToString:@"13780"]) { 153 | return @"image/png"; 154 | }else if ([numStr isEqualToString:@"7790"]) { 155 | return @"exe"; 156 | }else if ([numStr isEqualToString:@"8297"]) { 157 | return @"rar"; 158 | }else if ([numStr isEqualToString:@"8075"]) { 159 | return @"zip"; 160 | }else if ([numStr isEqualToString:@"55122"]) { 161 | return @"7z"; 162 | }else if ([numStr isEqualToString:@"6063"]) { 163 | return @"xml"; 164 | }else if ([numStr isEqualToString:@"6033"]) { 165 | return @"html"; 166 | }else if ([numStr isEqualToString:@"119105"]) { 167 | return @"js"; 168 | }else if ([numStr isEqualToString:@"102100"]) { 169 | return @"txt"; 170 | }else if ([numStr isEqualToString:@"255254"]) { 171 | return @"sql"; 172 | }else{ 173 | return @"未知文件类型"; 174 | } 175 | } 176 | 177 | 178 | 179 | 180 | @end 181 | -------------------------------------------------------------------------------- /SandBoxPreviewTool/Classes/LJ_FileInfoController.h: -------------------------------------------------------------------------------- 1 | // 2 | // LJ_FileInfoController.h 3 | // LJHotUpdate 4 | // 5 | // Created by nuomi on 2017/2/9. 6 | // Copyright © 2017年 xgyg. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LJ_FileInfoController : UIViewController 12 | 13 | @property (nonatomic,copy)NSString * filePath;//文件路径 14 | @property (nonatomic,copy)NSString * fileName;//文件名称 15 | @property (nonatomic,strong)NSDictionary * fileInfo;//文件名称 16 | //根据文件类型和路径创建 17 | + (instancetype)createWithFileName:(NSString *)fileName andFilePath:(NSString *)filePath andFileInfo:(NSDictionary *)fileInfo; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /SandBoxPreviewTool/Classes/LJ_FileInfoController.m: -------------------------------------------------------------------------------- 1 | // 2 | // LJ_FileInfoController.m 3 | // LJHotUpdate 4 | // 5 | // Created by nuomi on 2017/2/9. 6 | // Copyright © 2017年 xgyg. All rights reserved. 7 | // 8 | 9 | #import "LJ_FileInfoController.h" 10 | #import "LJ_FileInfo.h" 11 | #import "SandBoxPreviewTool.h" 12 | //屏幕最小值 13 | #define MIN_Screen ([UIScreen mainScreen].bounds.size.width < [UIScreen mainScreen].bounds.size.height ? [UIScreen mainScreen].bounds.size.width : [UIScreen mainScreen].bounds.size.height) 14 | 15 | @interface LJ_FileInfoController () 16 | 17 | @property (nonatomic,strong) UITextView * tView; 18 | @property (nonatomic,strong) UIWebView * webView; 19 | @property (nonatomic,copy) NSString * md5; 20 | 21 | @end 22 | 23 | @implementation LJ_FileInfoController 24 | 25 | + (instancetype)createWithFileName:(NSString *)fileName andFilePath:(NSString *)filePath andFileInfo:(NSDictionary *)fileInfo{ 26 | LJ_FileInfoController * infoVC = [[LJ_FileInfoController alloc] init]; 27 | infoVC.filePath = filePath; 28 | infoVC.fileName = fileName; 29 | infoVC.fileInfo = fileInfo; 30 | return infoVC; 31 | } 32 | 33 | - (void)viewDidLoad { 34 | [super viewDidLoad]; 35 | [self setUpUI]; 36 | #ifdef DEBUG 37 | if ([SandBoxPreviewTool sharedTool].openLog) { 38 | NSLog(@"%@",self.filePath); 39 | } 40 | #endif 41 | } 42 | 43 | - (void)setUpUI{ 44 | 45 | self.title = self.fileName; 46 | self.view.backgroundColor = [UIColor whiteColor]; 47 | self.edgesForExtendedLayout = UIRectEdgeNone;//禁止内容从bar底部开始布局 48 | 49 | UIBarButtonItem * right = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:@selector(shareFile)]; 50 | self.navigationItem.rightBarButtonItem = right; 51 | 52 | NSString * type= [[self.fileName componentsSeparatedByString:@"."] lastObject]; 53 | UIView * top_View = nil; 54 | if ([type isEqualToString:@"html"] || [type isEqualToString:@"js"] || [type isEqualToString:@"pdf"] || [type isEqualToString:@"docx"] || [type isEqualToString:@"xlsx"] || [type isEqualToString:@"ppt"] || [type isEqualToString:@"xlsx"] || [type isEqualToString:@"css"]) { 55 | //go webView when .js .html .ppt .pdf .docx .css file 56 | NSURLRequest * request = [NSURLRequest requestWithURL:[NSURL fileURLWithPath:self.filePath]]; 57 | [self.webView loadRequest:request]; 58 | top_View = self.webView; 59 | 60 | }else if ([type isEqualToString:@"h"] ||[type isEqualToString:@"m"] || [type isEqualToString:@"jsbundle"] || [type isEqualToString:@"log"]|| [type isEqualToString:@"txt"]) { 61 | //go textView when .h .m .jsbundle .log .txt file 62 | NSString * string = [NSString stringWithContentsOfFile:self.filePath encoding:NSUTF8StringEncoding error:nil]; 63 | self.tView.text = string; 64 | top_View = self.tView; 65 | }else if ([self.fileInfo[@"FileType"] hasPrefix:@"image"]){ 66 | 67 | //go imageView 68 | UIImage * image = [[UIImage alloc] initWithContentsOfFile:self.filePath]; 69 | UIImageView * imageView = [[UIImageView alloc] initWithImage:image]; 70 | imageView.translatesAutoresizingMaskIntoConstraints = NO; 71 | imageView.contentMode = UIViewContentModeScaleAspectFit; 72 | CGFloat width = MIN_Screen - 50; 73 | CGFloat height = (width/image.size.width) * image.size.height; 74 | 75 | if ((image.size.width < width && image.size.height width && image.size.height < 60)) {//如果图片的尺寸小于真实的尺寸 76 | CGFloat img_w = image.size.width; 77 | CGFloat img_h = image.size.height; 78 | if (img_w < 50 && img_h < 50) {//图片太小的情况下,略微放大显示 79 | img_w *= 2; 80 | img_h *= 2; 81 | }else if (image.size.width > width){ 82 | img_w = width; 83 | } 84 | UIView * bgView = [[UIView alloc] init]; 85 | [bgView addSubview:imageView]; 86 | NSLayoutConstraint * imgV_CenterX = [NSLayoutConstraint constraintWithItem:imageView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:bgView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]; 87 | NSLayoutConstraint * imgV_CenterY = [NSLayoutConstraint constraintWithItem:imageView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:bgView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0]; 88 | NSLayoutConstraint * imgV_W = [NSLayoutConstraint constraintWithItem:imageView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:img_w]; 89 | NSLayoutConstraint * imgV_H = [NSLayoutConstraint constraintWithItem:imageView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:img_h]; 90 | [bgView addConstraints:@[imgV_CenterX,imgV_CenterY,imgV_W,imgV_H]]; 91 | [self.view addSubview:bgView]; 92 | top_View = bgView; 93 | }else{ 94 | CGFloat margin_gap = 20; 95 | [self.view addSubview:imageView]; 96 | NSLayoutConstraint * imgV_Top = [NSLayoutConstraint constraintWithItem:imageView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:margin_gap]; 97 | if (image.size.width < width) { 98 | NSLayoutConstraint * imgV_CenterX = [NSLayoutConstraint constraintWithItem:imageView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]; 99 | NSLayoutConstraint * imgV_Width = [NSLayoutConstraint constraintWithItem:imageView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:image.size.width]; 100 | NSLayoutConstraint * imgV_Height = [NSLayoutConstraint constraintWithItem:imageView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:image.size.height]; 101 | [self.view addConstraints:@[imgV_CenterX,imgV_Width,imgV_Top,imgV_Height]]; 102 | }else{ 103 | NSLayoutConstraint * imgV_Left = [NSLayoutConstraint constraintWithItem:imageView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0 constant:margin_gap]; 104 | NSLayoutConstraint * imgV_Right = [NSLayoutConstraint constraintWithItem:imageView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeRight multiplier:1.0 constant:-margin_gap]; 105 | NSLayoutConstraint * imgV_Height = [NSLayoutConstraint constraintWithItem:imageView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:(height >= 1?height : 1)]; 106 | [self.view addConstraints:@[imgV_Left,imgV_Right,imgV_Top,imgV_Height]]; 107 | } 108 | top_View = imageView; 109 | } 110 | }else if( [type isEqualToString:@"plist"]){ 111 | //.plist 112 | NSMutableDictionary *dataDictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:self.filePath]; 113 | NSString *jsonString = [self changeDicToString:dataDictionary]; 114 | if (jsonString) { 115 | self.tView.text = jsonString; 116 | top_View = self.tView; 117 | }else{ 118 | UILabel * lb = [[UILabel alloc] init]; 119 | lb.text = @"数据为空哦~"; 120 | lb.textColor = [UIColor darkGrayColor]; 121 | lb.textAlignment = NSTextAlignmentCenter; 122 | [self.view addSubview:lb]; 123 | top_View = lb; 124 | } 125 | }else if( [type isEqualToString:@"json"]){ 126 | //.json 127 | NSData *data=[NSData dataWithContentsOfFile:self.filePath]; 128 | if(data){ 129 | NSError *error; 130 | NSDictionary * dataDictionary = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error]; 131 | NSString *jsonString = [self dictionaryToJson:dataDictionary]; 132 | if (jsonString && !error) { 133 | self.tView.text = jsonString; 134 | top_View = self.tView; 135 | }else{ 136 | NSURLRequest * request = [NSURLRequest requestWithURL:[NSURL fileURLWithPath:self.filePath]]; 137 | [self.webView loadRequest:request]; 138 | top_View = self.webView; 139 | } 140 | }else{ 141 | NSURLRequest * request = [NSURLRequest requestWithURL:[NSURL fileURLWithPath:self.filePath]]; 142 | [self.webView loadRequest:request]; 143 | top_View = self.webView; 144 | } 145 | }else{ 146 | NSError * error = nil; 147 | NSString * aString = [type isEqualToString:@"note"] ? nil : [NSString stringWithContentsOfFile:self.filePath encoding:NSUTF8StringEncoding error:&error]; 148 | if (error || !aString) { 149 | //other file ,not support 150 | UILabel * lb = [[UILabel alloc] init]; 151 | lb.text = @"暂不支持的文件格式"; 152 | lb.textColor = [UIColor darkGrayColor]; 153 | lb.textAlignment = NSTextAlignmentCenter; 154 | [self.view addSubview:lb]; 155 | top_View = lb; 156 | }else if(aString && [aString isKindOfClass:[NSString class]]){ 157 | NSDictionary * dict = [self parseJSONStringToNSDictionary:aString]; 158 | if (dict && [dict isKindOfClass:[NSDictionary class]]) { 159 | NSString * formatString = [self dictionaryToJson:dict]; 160 | if (formatString) { 161 | self.tView.text = formatString; 162 | top_View = self.tView; 163 | }else{ 164 | self.tView.text = aString; 165 | top_View = self.tView; 166 | } 167 | }else{ 168 | self.tView.text = aString; 169 | top_View = self.tView; 170 | } 171 | } 172 | } 173 | [self addFileInfoViewWithTopView:top_View]; 174 | } 175 | 176 | - (NSDictionary *)parseJSONStringToNSDictionary:(NSString *)JSONString { 177 | NSData *JSONData = [JSONString dataUsingEncoding:NSUTF8StringEncoding]; 178 | NSDictionary *responseJSON = [NSJSONSerialization JSONObjectWithData:JSONData options:NSJSONReadingMutableLeaves error:nil]; 179 | return responseJSON; 180 | } 181 | 182 | - (id)dictionaryToJson:(NSDictionary *)dic 183 | { 184 | if([dic isKindOfClass:[NSDictionary class]] && dic){ 185 | NSError *parseError = nil; 186 | NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:&parseError]; 187 | if (parseError || !jsonData) { 188 | return [self changeDicToString:dic]; 189 | } 190 | return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; 191 | }else{ 192 | return nil; 193 | } 194 | } 195 | 196 | 197 | //字段转换成格式化的字符串 198 | - (NSString *)changeDicToString:(NSDictionary *)dict{ 199 | if(!dict)return @""; 200 | NSMutableString * s = [NSMutableString string]; 201 | [s appendString:@"{\n"]; 202 | for (NSString * key in dict.allKeys) { 203 | NSString * value = dict[key]; 204 | [s appendString:[NSString stringWithFormat:@" %@ = %@;\n",key,value]]; 205 | } 206 | [s appendString:@"\n}\n"]; 207 | return s; 208 | } 209 | 210 | //分享文件 211 | - (void)shareFile{ 212 | NSURL *urlToShare = [NSURL fileURLWithPath:self.filePath]; 213 | if (!urlToShare) return; 214 | NSArray *activityItems = @[urlToShare]; 215 | UIActivityViewController *activityVC = [[UIActivityViewController alloc]initWithActivityItems:activityItems applicationActivities:nil]; 216 | [self.navigationController presentViewController:activityVC animated:YES completion:nil]; 217 | } 218 | 219 | 220 | - (void)addFileInfoViewWithTopView:(UIView *)topView{ 221 | //底部视图的高度是155 222 | UIView * bottomView = [[UIView alloc] init]; 223 | [self.view addSubview:bottomView]; 224 | bottomView.translatesAutoresizingMaskIntoConstraints = NO; 225 | NSLayoutConstraint * b_Left = [NSLayoutConstraint constraintWithItem:bottomView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0]; 226 | NSLayoutConstraint * b_Right = [NSLayoutConstraint constraintWithItem:bottomView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeRight multiplier:1.0 constant:0]; 227 | NSLayoutConstraint * b_Bottom = [NSLayoutConstraint constraintWithItem:bottomView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0]; 228 | NSLayoutConstraint * b_Height = [NSLayoutConstraint constraintWithItem:bottomView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:155]; 229 | 230 | 231 | //文件信息标签 232 | UILabel * gapLineView = [[UILabel alloc] init]; 233 | gapLineView.backgroundColor = [UIColor colorWithRed:0.94 green:0.94 blue:0.94 alpha:1]; 234 | gapLineView.text = @"文件信息"; 235 | gapLineView.font = [UIFont systemFontOfSize:14]; 236 | gapLineView.textColor = [UIColor colorWithRed:0.356 green:0.356 blue:0.356 alpha:1]; 237 | gapLineView.textAlignment = NSTextAlignmentCenter; 238 | [self.view addSubview:gapLineView]; 239 | gapLineView.translatesAutoresizingMaskIntoConstraints = NO; 240 | NSLayoutConstraint * gap_Left = [NSLayoutConstraint constraintWithItem:gapLineView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0]; 241 | NSLayoutConstraint * gap_Right = [NSLayoutConstraint constraintWithItem:gapLineView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeRight multiplier:1.0 constant:0]; 242 | NSLayoutConstraint * gap_Bottom = [NSLayoutConstraint constraintWithItem:gapLineView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:bottomView attribute:NSLayoutAttributeTop multiplier:1.0 constant:0]; 243 | NSLayoutConstraint * gap_Height = [NSLayoutConstraint constraintWithItem:gapLineView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:20]; 244 | if (![topView isKindOfClass:[UIImageView class]]) { 245 | [self.view addConstraints:@[b_Left,b_Right,b_Bottom,b_Height]]; 246 | [self.view addConstraints:@[gap_Left,gap_Right,gap_Bottom,gap_Height]]; 247 | }else if ([topView isKindOfClass:[UIImageView class]]){ 248 | NSLayoutConstraint * gap_Top = [NSLayoutConstraint constraintWithItem:gapLineView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:topView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:20]; 249 | NSLayoutConstraint * b_Top = [NSLayoutConstraint constraintWithItem:bottomView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:gapLineView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0]; 250 | [self.view addConstraints:@[gap_Left,gap_Right,gap_Top,gap_Height]]; 251 | [self.view addConstraints:@[b_Left,b_Right,b_Top,b_Height]]; 252 | } 253 | 254 | if (topView && ![topView isKindOfClass:[UIImageView class]]) { 255 | //顶部视图 256 | topView.translatesAutoresizingMaskIntoConstraints = NO; 257 | NSLayoutConstraint * topV_Left = [NSLayoutConstraint constraintWithItem:topView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0]; 258 | NSLayoutConstraint * topV_Right = [NSLayoutConstraint constraintWithItem:topView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeRight multiplier:1.0 constant:0]; 259 | NSLayoutConstraint * topV_Top = [NSLayoutConstraint constraintWithItem:topView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:0]; 260 | NSLayoutConstraint * topV_Bottom = [NSLayoutConstraint constraintWithItem:topView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:gapLineView attribute:NSLayoutAttributeTop multiplier:1.0 constant:0]; 261 | [self.view addConstraints:@[topV_Left,topV_Right,topV_Top,topV_Bottom]]; 262 | } 263 | 264 | NSString * fileSize, *fileModDate,*fileCreateDate, *fileMD5 = @""; 265 | //文件大小 266 | fileSize = [self.fileInfo objectForKey:NSFileSize]; 267 | CGFloat kb = [fileSize floatValue]/1024; 268 | if (kb < 1024) { 269 | fileSize = [NSString stringWithFormat:@"%.2f%@",kb,@"kb"]; 270 | }else{ 271 | fileSize = [NSString stringWithFormat:@"%.2f%@",kb/1024,@"M"]; 272 | } 273 | NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init]; 274 | [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; 275 | //文件创建日期 276 | fileCreateDate = [dateFormatter stringFromDate:self.fileInfo[NSFileCreationDate]]; 277 | //文件修改日期 278 | fileModDate = [dateFormatter stringFromDate:self.fileInfo[NSFileModificationDate]]; 279 | //md5 280 | fileMD5 = [self.filePath hasSuffix:@".note"] ? @"管道文件,无法获取MD5值" : [LJ_FileInfo getFileMD5WithPath:self.filePath]; 281 | self.md5 = fileMD5; 282 | NSArray * infoArr = @[fileMD5?:@"",fileSize?:@"",fileCreateDate?:@"",fileModDate?:@""]; 283 | NSArray * infoKeyArr = @[@"MD5值:",@"文件大小:",@"创建时间:",@"修改时间:"]; 284 | CGFloat height = 0; 285 | for (int i = 0; i < infoKeyArr.count; i++) { 286 | UIView * backView = [[UIView alloc] initWithFrame:CGRectMake(10, height , [UIScreen mainScreen].bounds.size.width - 20, 35)]; 287 | backView.clipsToBounds = YES; 288 | [bottomView addSubview:backView]; 289 | 290 | UILabel * desclb = [self createInfoLabelWithDesc:infoKeyArr[i]]; 291 | desclb.textAlignment = NSTextAlignmentRight; 292 | desclb.textColor = [UIColor lightGrayColor]; 293 | desclb.frame = CGRectMake(0, 0, 74, 35); 294 | [backView addSubview:desclb]; 295 | 296 | UILabel * contentlb = [self createInfoLabelWithDesc:infoArr[i]]; 297 | contentlb.textAlignment = NSTextAlignmentLeft; 298 | contentlb.textColor = [UIColor blackColor]; 299 | contentlb.frame = CGRectMake(74, 0, [UIScreen mainScreen].bounds.size.width - 94 , 35); 300 | [backView addSubview:contentlb]; 301 | if (i == 0) { 302 | contentlb.userInteractionEnabled = YES; 303 | UILongPressGestureRecognizer * longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(getMD5)]; 304 | longPress.minimumPressDuration = 0.5; 305 | [contentlb addGestureRecognizer:longPress]; 306 | } 307 | height += 40; 308 | } 309 | 310 | if([topView isKindOfClass:[UIImageView class]]){ 311 | [self.view layoutIfNeeded]; 312 | UIView * vi = self.view; 313 | UIScrollView * sc = [[UIScrollView alloc] initWithFrame:self.view.bounds]; 314 | sc.contentSize = CGSizeMake(vi.frame.size.width, bottomView.frame.origin.y + bottomView.frame.size.height); 315 | sc.showsVerticalScrollIndicator = NO; 316 | [sc addSubview:vi]; 317 | self.view = sc; 318 | } 319 | 320 | } 321 | 322 | 323 | - (void)getMD5{ 324 | [UIPasteboard generalPasteboard].string = self.md5; 325 | UIAlertController * alert = [UIAlertController alertControllerWithTitle:@"复制该文件MD5成功" message:self.md5 preferredStyle:UIAlertControllerStyleAlert]; 326 | [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil]]; 327 | [self.navigationController presentViewController:alert animated:YES completion:nil]; 328 | } 329 | 330 | - (UILabel *)createInfoLabelWithDesc:(NSString * )descStr{ 331 | UILabel * label = [[UILabel alloc] init]; 332 | label.text = descStr; 333 | label.numberOfLines = 1; 334 | label.font = [UIFont systemFontOfSize:14]; 335 | label.textAlignment = NSTextAlignmentRight; 336 | return label; 337 | } 338 | 339 | 340 | - (UITextView *)tView{ 341 | if (!_tView) { 342 | _tView = [[UITextView alloc] init]; 343 | _tView.editable = NO; 344 | _tView.backgroundColor = [UIColor whiteColor]; 345 | [self.view addSubview:_tView]; 346 | } 347 | return _tView; 348 | } 349 | 350 | - (UIWebView *)webView{ 351 | if (!_webView) { 352 | _webView = [[UIWebView alloc] init]; 353 | _webView.backgroundColor = [UIColor whiteColor]; 354 | [self.view addSubview:_webView]; 355 | } 356 | return _webView; 357 | } 358 | 359 | 360 | 361 | @end 362 | -------------------------------------------------------------------------------- /SandBoxPreviewTool/Classes/LJ_HomeDirViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // LJ_HomeDirViewController.h 3 | // FileDirectoryTool 4 | // 5 | // Created by nuomi on 2017/2/7. 6 | // Copyright © 2017年 xgyg. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LJ_HomeDirViewController : UIViewController 12 | 13 | @property (nonatomic,assign)BOOL isHomeDir;//文件路径 14 | @property (nonatomic,copy)NSString * filePath;//文件路径 15 | @property (nonatomic,copy)NSString * fileName;//文件名称 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /SandBoxPreviewTool/Classes/LJ_HomeDirViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // LJ_HomeDirViewController.m 3 | // FileDirectoryTool 4 | // 5 | // Created by nuomi on 2017/2/7. 6 | // Copyright © 2017年 xgyg. All rights reserved. 7 | // 8 | 9 | #import "LJ_HomeDirViewController.h" 10 | #import "LJ_FileInfoController.h" //文件详情页面 11 | #import "LJ_FileInfo.h" //文件检查类 12 | #import "SLUnilityObject.h" 13 | 14 | @interface LJ_HomeDirViewController () 15 | 16 | @property (nonatomic,strong)UITableView * tableView; 17 | @property (nonatomic,strong)NSMutableArray * dataSource; 18 | 19 | @end 20 | 21 | @implementation LJ_HomeDirViewController 22 | 23 | - (void)viewDidLoad { 24 | [super viewDidLoad]; 25 | [self setUpUI]; 26 | } 27 | 28 | - (void)close{ 29 | if (self.navigationController) { 30 | [self.navigationController dismissViewControllerAnimated:YES completion:nil]; 31 | } 32 | } 33 | 34 | - (void)setUpUI{ 35 | 36 | if (self.isHomeDir) { 37 | self.title = @"应用沙盒文件目录"; 38 | NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary]; 39 | // CFShow((__bridge CFTypeRef)(infoDictionary)); 40 | NSString *executableFile = [infoDictionary objectForKey:(NSString *)kCFBundleExecutableKey]; //获取项目包文件 41 | if(executableFile){ 42 | executableFile = [executableFile stringByAppendingString:@".app (包)"]; 43 | }else{ 44 | executableFile = @"app"; 45 | } 46 | [_dataSource addObject:@{@"title":executableFile,@"isDir":@NO,@"canDel":@NO}]; 47 | _filePath = NSHomeDirectory(); 48 | UIBarButtonItem * left = [[UIBarButtonItem alloc]initWithTitle:@"关闭" style:UIBarButtonItemStylePlain target:self action:@selector(close)]; 49 | self.navigationItem.leftBarButtonItem = left; 50 | }else{ 51 | self.title = self.fileName; 52 | UIBarButtonItem * right = [[UIBarButtonItem alloc]initWithTitle:@"刷新" style:UIBarButtonItemStylePlain target:self action:@selector(findAllFileInfo)]; 53 | self.navigationItem.rightBarButtonItem = right; 54 | } 55 | 56 | self.view.backgroundColor = [UIColor colorWithRed:0.945 green:0.945 blue:0.945 alpha:1]; 57 | self.automaticallyAdjustsScrollViewInsets = YES; 58 | self.tableView = [[UITableView alloc]initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height) style:UITableViewStylePlain]; 59 | self.tableView.delegate = self; 60 | self.tableView.dataSource = self; 61 | self.tableView.rowHeight = 60; 62 | self.tableView.separatorColor = [UIColor colorWithRed:0.898 green:0.898 blue:0.898 alpha:1]; 63 | self.tableView.tableFooterView = [UIView new]; 64 | [self.view addSubview:self.tableView]; 65 | [self.tableView setAutoresizingMask:UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight]; 66 | [self findAllFileInfo]; 67 | } 68 | 69 | - (void)findAllFileInfo{ 70 | 71 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 72 | self.dataSource = [LJ_FileInfo searchAllFileFromRightDirPath:self.filePath]; 73 | dispatch_async(dispatch_get_main_queue(), ^{ 74 | if (self.dataSource.count) { 75 | self.tableView.hidden = NO; 76 | [self.tableView reloadData]; 77 | }else{ 78 | self.tableView.hidden = YES; 79 | } 80 | }); 81 | }); 82 | } 83 | 84 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ 85 | return _dataSource.count; 86 | } 87 | 88 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ 89 | 90 | static NSString * cellID = @"LJ_HomeDirViewController_tableViewCell"; 91 | UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:cellID]; 92 | if (cell == nil) { 93 | cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellID]; 94 | } 95 | NSDictionary *infoDict = _dataSource[indexPath.row]; 96 | cell.textLabel.textColor = [UIColor colorWithRed:0.19 green:0.19 blue:0.19 alpha:1]; 97 | cell.textLabel.text = [infoDict objectForKey:@"title"]; 98 | cell.textLabel.font = [UIFont systemFontOfSize:15]; 99 | cell.separatorInset = UIEdgeInsetsMake(0, 15, 0, 0); 100 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 101 | if ([[infoDict objectForKey:NSFileType] isEqualToString:NSFileTypeDirectory] == NO) { 102 | //非文件夹 103 | cell.imageView.contentMode = UIViewContentModeScaleAspectFit; 104 | if ([infoDict[@"FileType"] hasPrefix:@"image"]) { 105 | //图片类型 106 | cell.imageView.image = [SLUnilityObject imageWithName:@"lj_Pictures"]; //[UIImage imageNamed:@"lj_Pictures"]; 107 | }else{ 108 | //unknow_icon 未知文件类型 109 | cell.imageView.image = [SLUnilityObject imageWithName:@"lj_unknow_icon"]; //[UIImage imageNamed:@"lj_unknow_icon"]; 110 | } 111 | }else{ 112 | //文件夹模式 113 | cell.imageView.image = [SLUnilityObject imageWithName:@"GenericFolderIcon"]; //[UIImage imageNamed:@"GenericFolderIcon.png"]; 114 | cell.imageView.contentMode = UIViewContentModeScaleAspectFit; 115 | } 116 | if(infoDict[NSFileModificationDate]){ 117 | NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init]; 118 | //[dateFormatter setDateFormat:@"最近修改时间:yyyy-MM-dd HH:mm:ss"]; 119 | [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; 120 | NSString *dateStr = [dateFormatter stringFromDate:infoDict[NSFileModificationDate]]; 121 | cell.detailTextLabel.text = dateStr; 122 | }else{ 123 | cell.detailTextLabel.text = @" "; 124 | } 125 | 126 | return cell; 127 | } 128 | 129 | - (nullable NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath { 130 | 131 | if (self.isHomeDir) { 132 | return @[]; 133 | } 134 | 135 | BOOL canDelete = [[NSFileManager defaultManager] isDeletableFileAtPath:self.filePath]; 136 | NSString * title = canDelete ? @"删除" : @"不可删除"; 137 | UITableViewRowAction * delete = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDestructive title:title handler:^(UITableViewRowAction * _Nonnull action, NSIndexPath * _Nonnull indexPath) { 138 | if ([action.title isEqualToString:@"删除"]) { 139 | [self deleteOneRowWithIndexPath:indexPath]; 140 | }else{ 141 | [self.tableView beginUpdates]; 142 | [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; 143 | [self.tableView endUpdates]; 144 | } 145 | }]; 146 | 147 | return @[delete]; 148 | } 149 | #pragma mark - 删除文件或者目录 150 | - (void)deleteOneRowWithIndexPath:(NSIndexPath *)indexPath{ 151 | 152 | //获取文件属性 153 | NSFileManager * filemager = [NSFileManager defaultManager]; 154 | NSDictionary * info = _dataSource[indexPath.row]; 155 | NSString * path = [self.filePath stringByAppendingPathComponent:info[@"title"]]; 156 | NSError * error = nil; 157 | [filemager removeItemAtPath:path error:&error]; 158 | if (error) { 159 | UIAlertController * alert = [UIAlertController alertControllerWithTitle:@"删除文件失败" message:nil preferredStyle:UIAlertControllerStyleAlert]; 160 | [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil]]; 161 | [self.navigationController presentViewController:alert animated:YES completion:nil]; 162 | return ; 163 | } 164 | 165 | NSMutableArray * ds = [NSMutableArray arrayWithArray:self.dataSource]; 166 | [ds removeObjectAtIndex:indexPath.row]; 167 | self.dataSource = ds; 168 | [self.tableView beginUpdates]; 169 | [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; 170 | [self.tableView endUpdates]; 171 | } 172 | 173 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ 174 | 175 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 176 | 177 | NSDictionary * infoDict = _dataSource[indexPath.row]; 178 | if ([[infoDict objectForKey:NSFileType] isEqualToString:NSFileTypeDirectory]) { 179 | LJ_HomeDirViewController * dirVC = [[LJ_HomeDirViewController alloc] init]; 180 | dirVC.filePath = [self.filePath stringByAppendingPathComponent:infoDict[@"title"]]; 181 | dirVC.fileName = infoDict[@"title"]; 182 | [self.navigationController pushViewController:dirVC animated:YES]; 183 | }else{ 184 | NSString * fileName = infoDict[@"title"]; 185 | NSString * filePath = [self.filePath stringByAppendingPathComponent:fileName]; 186 | 187 | LJ_FileInfoController * infovc =[LJ_FileInfoController createWithFileName:fileName andFilePath:filePath andFileInfo:infoDict]; 188 | [self.navigationController pushViewController:infovc animated:YES]; 189 | 190 | } 191 | } 192 | 193 | 194 | - (void)didReceiveMemoryWarning { 195 | [super didReceiveMemoryWarning]; 196 | // Dispose of any resources that can be recreated. 197 | } 198 | 199 | 200 | @end 201 | -------------------------------------------------------------------------------- /SandBoxPreviewTool/Classes/SLUnilityObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // SLUnilityObject.h 3 | // 沙盒查看工具 4 | // 5 | // Created by zhengxin on 2018/4/23. 6 | // Copyright © 2018 魏家园潇. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface SLUnilityObject : NSObject 13 | 14 | + (nullable NSBundle *)resourcesBundle; 15 | + (nullable UIImage *)imageWithName:(nullable NSString *)name; 16 | 17 | + (nullable NSBundle *)resourcesBundleWithName:(nullable NSString *)bundleName; 18 | + (nullable UIImage *)imageInBundle:(nullable NSBundle *)bundle withName:(nullable NSString *)name; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /SandBoxPreviewTool/Classes/SLUnilityObject.m: -------------------------------------------------------------------------------- 1 | // 2 | // SLUnilityObject.m 3 | // 沙盒查看工具 4 | // 5 | // Created by zhengxin on 2018/4/23. 6 | // Copyright © 2018 魏家园潇. All rights reserved. 7 | // 8 | 9 | #import "SLUnilityObject.h" 10 | 11 | NSString *const QMUIResourcesMainBundleName = @"SandBoxPreviewTool.bundle"; 12 | 13 | @implementation SLUnilityObject 14 | 15 | + (UIImage *)imageWithName:(NSString *)name { 16 | NSBundle *bundle = [SLUnilityObject resourcesBundle]; 17 | return [SLUnilityObject imageInBundle:bundle withName:name]; 18 | } 19 | 20 | + (NSBundle *)resourcesBundle { 21 | return [SLUnilityObject resourcesBundleWithName:QMUIResourcesMainBundleName]; 22 | } 23 | 24 | + (NSBundle *)resourcesBundleWithName:(NSString *)bundleName { 25 | NSBundle *bundle = [NSBundle bundleWithPath: [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:bundleName]]; 26 | if (!bundle) { 27 | // 动态framework的bundle资源是打包在framework里面的,所以无法通过mainBundle拿到资源,只能通过其他方法来获取bundle资源。 28 | NSBundle *frameworkBundle = [NSBundle bundleForClass:[self class]]; 29 | NSDictionary *bundleData = [self parseBundleName:bundleName]; 30 | if (bundleData) { 31 | bundle = [NSBundle bundleWithPath:[frameworkBundle pathForResource:[bundleData objectForKey:@"name"] ofType:[bundleData objectForKey:@"type"]]]; 32 | } 33 | } 34 | return bundle; 35 | } 36 | 37 | + (UIImage *)imageInBundle:(NSBundle *)bundle withName:(NSString *)name { 38 | if (bundle && name) { 39 | if ([UIImage respondsToSelector:@selector(imageNamed:inBundle:compatibleWithTraitCollection:)]) { 40 | return [UIImage imageNamed:name inBundle:bundle compatibleWithTraitCollection:nil]; 41 | } else { 42 | NSString *imagePath = [[bundle resourcePath] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.png", name]]; 43 | return [UIImage imageWithContentsOfFile:imagePath]; 44 | } 45 | } 46 | return nil; 47 | } 48 | 49 | + (NSDictionary *)parseBundleName:(NSString *)bundleName { 50 | NSArray *bundleData = [bundleName componentsSeparatedByString:@"."]; 51 | if (bundleData.count == 2) { 52 | return @{@"name":bundleData[0], @"type":bundleData[1]}; 53 | } 54 | return nil; 55 | } 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /SandBoxPreviewTool/Classes/SandBoxPreviewTool.h: -------------------------------------------------------------------------------- 1 | // 2 | // LJ_FileTool.h 3 | // FileDirectoryTool 4 | // 5 | // Created by nuomi on 2017/2/7. 6 | // Copyright © 2017年 xgyg. All rights reserved. 7 | // 沙盒预览工具 程序磁盘文件调试系统 8 | // Usage 9 | /* 10 | step1: 11 | #import "SandBoxPreviewTool.h" 12 | 13 | step2: 14 | [[SandBoxPreviewTool sharedTool] autoOpenCloseApplicationDiskDirectoryPanel]; 15 | */ 16 | 17 | #import 18 | 19 | @interface SandBoxPreviewTool : NSObject 20 | 21 | 22 | @property (nonatomic,assign)BOOL openLog;//开启log打印文件路径 23 | 24 | //单例 25 | + (instancetype)sharedTool; 26 | 27 | 28 | //自动打开或关闭应用磁盘目录面板 29 | - (void)autoOpenCloseApplicationDiskDirectoryPanel; 30 | 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /SandBoxPreviewTool/Classes/SandBoxPreviewTool.m: -------------------------------------------------------------------------------- 1 | // 2 | // LJ_FileTool.m 3 | // FileDirectoryTool 4 | // 5 | // Created by nuomi on 2017/2/7. 6 | // Copyright © 2017年 xgyg. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SandBoxPreviewTool.h" 11 | #import "LJ_DirToolNavigatorController.h" 12 | 13 | @interface SandBoxPreviewTool () 14 | 15 | @property (nonatomic,strong)LJ_DirToolNavigatorController * navVC; 16 | 17 | @end 18 | 19 | @implementation SandBoxPreviewTool 20 | 21 | static SandBoxPreviewTool *_singleton; 22 | 23 | + (instancetype)sharedTool{ 24 | static dispatch_once_t onceToken; 25 | dispatch_once(&onceToken, ^{ 26 | _singleton = [[self alloc] init];; 27 | }); 28 | return _singleton; 29 | } 30 | 31 | + (instancetype)allocWithZone:(struct _NSZone *)zone{ 32 | static dispatch_once_t onceToken; 33 | dispatch_once(&onceToken, ^{ 34 | _singleton = [super allocWithZone:zone]; 35 | }); 36 | return _singleton; 37 | } 38 | 39 | #pragma mark 打开或关闭应用磁盘目录面板 40 | - (void)autoOpenCloseApplicationDiskDirectoryPanel{ 41 | UIViewController *root = UIApplication.sharedApplication.windows[0].rootViewController; 42 | BOOL isEqual = root.presentedViewController == _navVC; 43 | if (root.presentedViewController) { 44 | if (isEqual) {//相同则直接隐藏 不相同,隐藏后再弹出 45 | [_navVC dismissViewControllerAnimated:YES completion:nil]; 46 | }else{ 47 | [root.presentedViewController dismissViewControllerAnimated:YES completion:^{ 48 | [self presentNav]; 49 | }]; 50 | } 51 | }else{//直接弹出 52 | [self presentNav]; 53 | } 54 | } 55 | 56 | - (void)presentNav{ 57 | if (_navVC) { 58 | [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:_navVC animated:YES completion:nil]; 59 | }else{ 60 | LJ_DirToolNavigatorController * vc = [LJ_DirToolNavigatorController create]; 61 | vc.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal; 62 | [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:vc animated:YES completion:nil]; 63 | _navVC = vc; 64 | } 65 | } 66 | 67 | 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /SandBoxPreviewTool/Classes/SuspensionButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by wjyx on 2017/6/4. 3 | // 悬浮按钮 4 | // 5 | 6 | #import 7 | 8 | typedef NS_ENUM(NSUInteger, SuspensionViewLeanType) { 9 | /** Can only stay in the left and right */ 10 | SuspensionViewLeanTypeHorizontal, 11 | /** Can stay in the upper, lower, left, right */ 12 | SuspensionViewLeanTypeEachSide 13 | }; 14 | 15 | 16 | @interface SuspensionButton : UIButton 17 | 18 | @property (nonatomic,assign)SuspensionViewLeanType leanType; 19 | 20 | - (instancetype)initWithFrame:(CGRect)frame color:(UIColor*)color; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /SandBoxPreviewTool/Classes/SuspensionButton.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by wjyx on 2017/6/4. 3 | // 4 | // 5 | 6 | #import "SuspensionButton.h" 7 | 8 | 9 | @interface SuspensionButton () 10 | @property (nonatomic,strong)UIColor * normalColor; 11 | @end 12 | 13 | @implementation SuspensionButton 14 | 15 | double radians(float degrees) { 16 | return ( degrees * 3.14159265 ) / 180.0; 17 | } 18 | 19 | 20 | - (instancetype)initWithFrame:(CGRect)frame color:(UIColor*)color 21 | { 22 | if(self = [super initWithFrame:frame]){ 23 | self.userInteractionEnabled = YES; 24 | self.backgroundColor = [UIColor blackColor]; 25 | self.normalColor = color; 26 | self.alpha = .7; 27 | self.titleLabel.font = [UIFont systemFontOfSize:14]; 28 | CGFloat min = frame.size.width <= frame.size.height ? frame.size.width : frame.size.height ; 29 | self.layer.cornerRadius = min / 2.0; 30 | self.clipsToBounds = YES; 31 | 32 | [self creatWithGap:5 andCallBack:nil]; 33 | [self creatWithGap:11 andCallBack:nil]; 34 | [self creatWithGap:17 andCallBack:^(CAShapeLayer *layer) { 35 | CALayer *lineLayer1 = [CALayer layer]; 36 | lineLayer1.backgroundColor = [[UIColor whiteColor] CGColor]; 37 | lineLayer1.frame = CGRectMake(3,15.5, min - 23 , 2); 38 | lineLayer1.cornerRadius = 1; 39 | [layer addSublayer:lineLayer1]; 40 | 41 | CALayer *lineLayer2 = [CALayer layer]; 42 | lineLayer2.backgroundColor = [[UIColor whiteColor] CGColor]; 43 | lineLayer2.frame = CGRectMake(15.5, 3,2, min - 23 ); 44 | [layer addSublayer:lineLayer2]; 45 | lineLayer2.cornerRadius = 1; 46 | 47 | layer.transform = CATransform3DMakeRotation(radians(45), 0, 0, 1); 48 | }]; 49 | UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(handlePanGesture:)]; 50 | pan.delaysTouchesBegan = YES; 51 | [self addGestureRecognizer:pan]; 52 | 53 | } 54 | return self; 55 | } 56 | 57 | - (void)creatWithGap:(CGFloat)gap andCallBack:(void(^)(CAShapeLayer * layer))callBack{ 58 | CGFloat min = self.frame.size.width <= self.frame.size.height ? self.frame.size.width : self.frame.size.height ; 59 | CAShapeLayer * ringShapeLayer = [[CAShapeLayer alloc] init]; 60 | ringShapeLayer.bounds = CGRectMake(0, 0, min - gap, min -gap); 61 | ringShapeLayer.fillColor = [UIColor clearColor].CGColor; 62 | ringShapeLayer.lineWidth = 1.5; 63 | ringShapeLayer.strokeColor = [UIColor whiteColor].CGColor; 64 | CGRect frame = ringShapeLayer.bounds; 65 | UIBezierPath *circlePath = [UIBezierPath bezierPathWithOvalInRect:frame]; 66 | ringShapeLayer.path = circlePath.CGPath; 67 | ringShapeLayer.position = CGPointMake(min/2,min/2); 68 | [self.layer addSublayer:ringShapeLayer]; 69 | if (callBack) { 70 | callBack(ringShapeLayer); 71 | } 72 | } 73 | 74 | 75 | #pragma mark - event response 76 | - (void)handlePanGesture:(UIPanGestureRecognizer*)p 77 | { 78 | UIWindow *appWindow = [UIApplication sharedApplication].delegate.window; 79 | CGPoint panPoint = [p locationInView:appWindow]; 80 | if(p.state == UIGestureRecognizerStateBegan) { 81 | self.alpha = 1; 82 | self.backgroundColor = self.normalColor; 83 | }else if(p.state == UIGestureRecognizerStateChanged) { 84 | self.center = CGPointMake(panPoint.x, panPoint.y); 85 | }else if(p.state == UIGestureRecognizerStateEnded 86 | || p.state == UIGestureRecognizerStateCancelled) { 87 | self.alpha = .7; 88 | self.backgroundColor = [UIColor blackColor]; 89 | CGFloat ballWidth = self.frame.size.width; 90 | CGFloat ballHeight = self.frame.size.height; 91 | CGFloat screenWidth = [[UIScreen mainScreen] bounds].size.width; 92 | CGFloat screenHeight = [[UIScreen mainScreen] bounds].size.height; 93 | 94 | CGFloat left = fabs(panPoint.x); 95 | CGFloat right = fabs(screenWidth - left); 96 | CGFloat top = fabs(panPoint.y); 97 | CGFloat bottom = fabs(screenHeight - top); 98 | 99 | CGFloat minSpace = 0; 100 | if (self.leanType == SuspensionViewLeanTypeHorizontal) { 101 | minSpace = MIN(left, right); 102 | }else{ 103 | minSpace = MIN(MIN(MIN(top, left), bottom), right); 104 | } 105 | CGPoint newCenter = CGPointZero; 106 | CGFloat targetY = 0; 107 | 108 | //Correcting Y 109 | if (panPoint.y < 15 + ballHeight / 2.0) { 110 | targetY = 15 + ballHeight / 2.0; 111 | }else if (panPoint.y > (screenHeight - ballHeight / 2.0 - 15)) { 112 | targetY = screenHeight - ballHeight / 2.0 - 15; 113 | }else{ 114 | targetY = panPoint.y; 115 | } 116 | 117 | if (minSpace == left) { 118 | newCenter = CGPointMake(ballHeight / 3, targetY); 119 | }else if (minSpace == right) { 120 | newCenter = CGPointMake(screenWidth - ballHeight / 3, targetY); 121 | }else if (minSpace == top) { 122 | newCenter = CGPointMake(panPoint.x, ballWidth / 3); 123 | }else { 124 | newCenter = CGPointMake(panPoint.x, screenHeight - ballWidth / 3); 125 | } 126 | 127 | [UIView animateWithDuration:.25 animations:^{ 128 | self.center = newCenter; 129 | }]; 130 | }else{ 131 | self.alpha = .7; 132 | } 133 | } 134 | 135 | 136 | 137 | 138 | @end 139 | -------------------------------------------------------------------------------- /_Pods.xcodeproj: -------------------------------------------------------------------------------- 1 | Example/Pods/Pods.xcodeproj --------------------------------------------------------------------------------