├── LICENSE ├── README.md ├── ShotBlocker.podspec ├── ShotBlocker.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── clayallsopp.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── clayallsopp.xcuserdatad │ ├── xcdebugger │ └── Breakpoints.xcbkptlist │ └── xcschemes │ ├── ShotBlocker.xcscheme │ └── xcschememanagement.plist ├── ShotBlocker ├── ShotBlocker.h └── ShotBlocker.m └── ShotBlockerExample ├── CYAppDelegate.h ├── CYAppDelegate.m ├── CYViewController.h ├── CYViewController.m ├── Default-568h@2x.png ├── Default.png ├── Default@2x.png ├── ShotBlocker-Info.plist ├── ShotBlocker-Prefix.pch ├── en.lproj ├── CYViewController.xib └── InfoPlist.strings └── main.m /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Clay Allsopp 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ShotBlocker 2 | 3 | Detecting iOS screenshots ala Snapchat and Facebook Poke. 4 | 5 | Current technique is to poll the user's camera roll and check for new screenshot-esque images; if you would like to add another technique, definitely submit a pull-request! 6 | 7 | ## Usage 8 | 9 | ```objective-c 10 | [[ShotBlocker sharedManager] detectScreenshotWithImageBlock:^(UIImage *screenshot) { 11 | NSLog(@"Screenshot: %@", screenshot); 12 | }]; 13 | 14 | // Later on... 15 | 16 | [[ShotBlocker sharedManager] stopDetectingScreenshots]; 17 | ``` 18 | 19 | Also available are: 20 | 21 | - `detectScreenshotWithBlock:^()` 22 | - `detectScreenshotWithBlock:^() andErrorBlock:^(NSError * error){}` 23 | - `detectScreenshotWithImageBlock:^(UIImage *screenshot) andErrorBlock:^(NSError * error){}` 24 | 25 | The `NSError` will occur if the user denies your app access to their photos. 26 | 27 | ## Installation 28 | 29 | ### [CocoaPods](http://cocoapods.org/) 30 | 31 | ```ruby 32 | pod 'ShotBlocker' 33 | ``` 34 | 35 | ```objective-c 36 | #import 37 | ``` 38 | 39 | ### Xcode 40 | 41 | 1. Add ShotBlocker as a [git submodule](http://schacon.github.com/git/user-manual.html#submodules). Here's how to add it as a submodule: 42 | 43 | ``` 44 | $ cd rootOfYourGitRepo 45 | $ git submodule add https://github.com/clayallsopp/ShotBlocker.git Vendor/ShotBlocker 46 | $ git submodule update --init --recursive 47 | ``` 48 | 49 | 2. Add `ShotBlocker/ShotBlocker.h` and `ShotBlocker/ShotBlocker.m` to your project, but don't copy the files (so the location is relative). 50 | 51 | 3. Add `AssetsLibrary.framework` to your project 52 | 53 | ```objective-c 54 | #import "ShotBlocker.h" 55 | ``` 56 | 57 | ### Contact 58 | 59 | [Clay Allsopp](http://clayallsopp.com/) 60 | [@clayallsopp](https://twitter.com/clayallsopp) 61 | -------------------------------------------------------------------------------- /ShotBlocker.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "ShotBlocker" 3 | s.version = "0.0.3" 4 | s.summary = "Detecting iOS screenshots ala Snapchat and Facebook Poke." 5 | s.homepage = "https://github.com/clayallsopp/shotblocker" 6 | s.author = { "Clay Allsopp" => "clay.allsopp@gmail.com" } 7 | s.source = { :git => "https://github.com/clayallsopp/ShotBlocker.git", :tag => "0.0.3" } 8 | s.platform = :ios, '5.0' 9 | s.source_files = 'ShotBlocker/*.{h,m}' 10 | s.frameworks = 'AssetsLibrary' 11 | s.requires_arc = true 12 | s.license = { :type => 'MIT', :file => 'LICENSE' } 13 | end 14 | -------------------------------------------------------------------------------- /ShotBlocker.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | F8C0EABE168B941A00209C5D /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8C0EABD168B941A00209C5D /* UIKit.framework */; }; 11 | F8C0EAC0168B941A00209C5D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8C0EABF168B941A00209C5D /* Foundation.framework */; }; 12 | F8C0EAC2168B941A00209C5D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8C0EAC1168B941A00209C5D /* CoreGraphics.framework */; }; 13 | F8C0EAED168B963700209C5D /* AssetsLibrary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8C0EAEC168B963700209C5D /* AssetsLibrary.framework */; }; 14 | F8C0EAF1168C984C00209C5D /* ShotBlocker.m in Sources */ = {isa = PBXBuildFile; fileRef = F8C0EAF0168C984C00209C5D /* ShotBlocker.m */; }; 15 | F8C0EAF6168C986000209C5D /* CYAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F8C0EAF3168C986000209C5D /* CYAppDelegate.m */; }; 16 | F8C0EAF7168C986000209C5D /* CYViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F8C0EAF5168C986000209C5D /* CYViewController.m */; }; 17 | F8C0EB04168C989000209C5D /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F8C0EAFE168C989000209C5D /* Default-568h@2x.png */; }; 18 | F8C0EB05168C989000209C5D /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = F8C0EAFF168C989000209C5D /* Default.png */; }; 19 | F8C0EB06168C989000209C5D /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F8C0EB00168C989000209C5D /* Default@2x.png */; }; 20 | F8C0EB07168C989000209C5D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F8C0EB01168C989000209C5D /* main.m */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXFileReference section */ 24 | F8C0EAB9168B941A00209C5D /* ShotBlocker.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ShotBlocker.app; sourceTree = BUILT_PRODUCTS_DIR; }; 25 | F8C0EABD168B941A00209C5D /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 26 | F8C0EABF168B941A00209C5D /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 27 | F8C0EAC1168B941A00209C5D /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 28 | F8C0EAEC168B963700209C5D /* AssetsLibrary.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AssetsLibrary.framework; path = System/Library/Frameworks/AssetsLibrary.framework; sourceTree = SDKROOT; }; 29 | F8C0EAEF168C984C00209C5D /* ShotBlocker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ShotBlocker.h; path = ShotBlocker/ShotBlocker.h; sourceTree = ""; }; 30 | F8C0EAF0168C984C00209C5D /* ShotBlocker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ShotBlocker.m; path = ShotBlocker/ShotBlocker.m; sourceTree = ""; }; 31 | F8C0EAF2168C986000209C5D /* CYAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CYAppDelegate.h; path = ShotBlockerExample/CYAppDelegate.h; sourceTree = SOURCE_ROOT; }; 32 | F8C0EAF3168C986000209C5D /* CYAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CYAppDelegate.m; path = ShotBlockerExample/CYAppDelegate.m; sourceTree = SOURCE_ROOT; }; 33 | F8C0EAF4168C986000209C5D /* CYViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CYViewController.h; path = ShotBlockerExample/CYViewController.h; sourceTree = SOURCE_ROOT; }; 34 | F8C0EAF5168C986000209C5D /* CYViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CYViewController.m; path = ShotBlockerExample/CYViewController.m; sourceTree = SOURCE_ROOT; }; 35 | F8C0EAF9168C987700209C5D /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = ShotBlockerExample/en.lproj/CYViewController.xib; sourceTree = SOURCE_ROOT; }; 36 | F8C0EAFC168C987E00209C5D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = ShotBlockerExample/en.lproj/InfoPlist.strings; sourceTree = SOURCE_ROOT; }; 37 | F8C0EAFE168C989000209C5D /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default-568h@2x.png"; path = "ShotBlockerExample/Default-568h@2x.png"; sourceTree = SOURCE_ROOT; }; 38 | F8C0EAFF168C989000209C5D /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Default.png; path = ShotBlockerExample/Default.png; sourceTree = SOURCE_ROOT; }; 39 | F8C0EB00168C989000209C5D /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default@2x.png"; path = "ShotBlockerExample/Default@2x.png"; sourceTree = SOURCE_ROOT; }; 40 | F8C0EB01168C989000209C5D /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = ShotBlockerExample/main.m; sourceTree = SOURCE_ROOT; }; 41 | F8C0EB02168C989000209C5D /* ShotBlocker-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "ShotBlocker-Info.plist"; path = "ShotBlockerExample/ShotBlocker-Info.plist"; sourceTree = SOURCE_ROOT; }; 42 | F8C0EB03168C989000209C5D /* ShotBlocker-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "ShotBlocker-Prefix.pch"; path = "ShotBlockerExample/ShotBlocker-Prefix.pch"; sourceTree = SOURCE_ROOT; }; 43 | /* End PBXFileReference section */ 44 | 45 | /* Begin PBXFrameworksBuildPhase section */ 46 | F8C0EAB6168B941A00209C5D /* Frameworks */ = { 47 | isa = PBXFrameworksBuildPhase; 48 | buildActionMask = 2147483647; 49 | files = ( 50 | F8C0EAED168B963700209C5D /* AssetsLibrary.framework in Frameworks */, 51 | F8C0EABE168B941A00209C5D /* UIKit.framework in Frameworks */, 52 | F8C0EAC0168B941A00209C5D /* Foundation.framework in Frameworks */, 53 | F8C0EAC2168B941A00209C5D /* CoreGraphics.framework in Frameworks */, 54 | ); 55 | runOnlyForDeploymentPostprocessing = 0; 56 | }; 57 | /* End PBXFrameworksBuildPhase section */ 58 | 59 | /* Begin PBXGroup section */ 60 | F8C0EAAE168B941A00209C5D = { 61 | isa = PBXGroup; 62 | children = ( 63 | F8C0EAEE168C980400209C5D /* ShotBlocker */, 64 | F8C0EAC3168B941A00209C5D /* ShotBlockerExample */, 65 | F8C0EABC168B941A00209C5D /* Frameworks */, 66 | F8C0EABA168B941A00209C5D /* Products */, 67 | ); 68 | sourceTree = ""; 69 | }; 70 | F8C0EABA168B941A00209C5D /* Products */ = { 71 | isa = PBXGroup; 72 | children = ( 73 | F8C0EAB9168B941A00209C5D /* ShotBlocker.app */, 74 | ); 75 | name = Products; 76 | sourceTree = ""; 77 | }; 78 | F8C0EABC168B941A00209C5D /* Frameworks */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | F8C0EAEC168B963700209C5D /* AssetsLibrary.framework */, 82 | F8C0EABD168B941A00209C5D /* UIKit.framework */, 83 | F8C0EABF168B941A00209C5D /* Foundation.framework */, 84 | F8C0EAC1168B941A00209C5D /* CoreGraphics.framework */, 85 | ); 86 | name = Frameworks; 87 | sourceTree = ""; 88 | }; 89 | F8C0EAC3168B941A00209C5D /* ShotBlockerExample */ = { 90 | isa = PBXGroup; 91 | children = ( 92 | F8C0EAF2168C986000209C5D /* CYAppDelegate.h */, 93 | F8C0EAF3168C986000209C5D /* CYAppDelegate.m */, 94 | F8C0EAF4168C986000209C5D /* CYViewController.h */, 95 | F8C0EAF5168C986000209C5D /* CYViewController.m */, 96 | F8C0EAF8168C987700209C5D /* CYViewController.xib */, 97 | F8C0EAC4168B941A00209C5D /* Supporting Files */, 98 | ); 99 | name = ShotBlockerExample; 100 | path = ShotBlocker; 101 | sourceTree = ""; 102 | }; 103 | F8C0EAC4168B941A00209C5D /* Supporting Files */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | F8C0EAFE168C989000209C5D /* Default-568h@2x.png */, 107 | F8C0EAFF168C989000209C5D /* Default.png */, 108 | F8C0EB00168C989000209C5D /* Default@2x.png */, 109 | F8C0EB01168C989000209C5D /* main.m */, 110 | F8C0EB02168C989000209C5D /* ShotBlocker-Info.plist */, 111 | F8C0EB03168C989000209C5D /* ShotBlocker-Prefix.pch */, 112 | F8C0EAFB168C987E00209C5D /* InfoPlist.strings */, 113 | ); 114 | name = "Supporting Files"; 115 | sourceTree = ""; 116 | }; 117 | F8C0EAEE168C980400209C5D /* ShotBlocker */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | F8C0EAEF168C984C00209C5D /* ShotBlocker.h */, 121 | F8C0EAF0168C984C00209C5D /* ShotBlocker.m */, 122 | ); 123 | name = ShotBlocker; 124 | sourceTree = ""; 125 | }; 126 | /* End PBXGroup section */ 127 | 128 | /* Begin PBXNativeTarget section */ 129 | F8C0EAB8168B941A00209C5D /* ShotBlocker */ = { 130 | isa = PBXNativeTarget; 131 | buildConfigurationList = F8C0EADD168B941A00209C5D /* Build configuration list for PBXNativeTarget "ShotBlocker" */; 132 | buildPhases = ( 133 | F8C0EAB5168B941A00209C5D /* Sources */, 134 | F8C0EAB6168B941A00209C5D /* Frameworks */, 135 | F8C0EAB7168B941A00209C5D /* Resources */, 136 | ); 137 | buildRules = ( 138 | ); 139 | dependencies = ( 140 | ); 141 | name = ShotBlocker; 142 | productName = ShotBlocker; 143 | productReference = F8C0EAB9168B941A00209C5D /* ShotBlocker.app */; 144 | productType = "com.apple.product-type.application"; 145 | }; 146 | /* End PBXNativeTarget section */ 147 | 148 | /* Begin PBXProject section */ 149 | F8C0EAB0168B941A00209C5D /* Project object */ = { 150 | isa = PBXProject; 151 | attributes = { 152 | CLASSPREFIX = CY; 153 | LastUpgradeCheck = 0450; 154 | ORGANIZATIONNAME = "Clay Allsopp"; 155 | }; 156 | buildConfigurationList = F8C0EAB3168B941A00209C5D /* Build configuration list for PBXProject "ShotBlocker" */; 157 | compatibilityVersion = "Xcode 3.2"; 158 | developmentRegion = English; 159 | hasScannedForEncodings = 0; 160 | knownRegions = ( 161 | en, 162 | ); 163 | mainGroup = F8C0EAAE168B941A00209C5D; 164 | productRefGroup = F8C0EABA168B941A00209C5D /* Products */; 165 | projectDirPath = ""; 166 | projectRoot = ""; 167 | targets = ( 168 | F8C0EAB8168B941A00209C5D /* ShotBlocker */, 169 | ); 170 | }; 171 | /* End PBXProject section */ 172 | 173 | /* Begin PBXResourcesBuildPhase section */ 174 | F8C0EAB7168B941A00209C5D /* Resources */ = { 175 | isa = PBXResourcesBuildPhase; 176 | buildActionMask = 2147483647; 177 | files = ( 178 | F8C0EB04168C989000209C5D /* Default-568h@2x.png in Resources */, 179 | F8C0EB05168C989000209C5D /* Default.png in Resources */, 180 | F8C0EB06168C989000209C5D /* Default@2x.png in Resources */, 181 | ); 182 | runOnlyForDeploymentPostprocessing = 0; 183 | }; 184 | /* End PBXResourcesBuildPhase section */ 185 | 186 | /* Begin PBXSourcesBuildPhase section */ 187 | F8C0EAB5168B941A00209C5D /* Sources */ = { 188 | isa = PBXSourcesBuildPhase; 189 | buildActionMask = 2147483647; 190 | files = ( 191 | F8C0EAF1168C984C00209C5D /* ShotBlocker.m in Sources */, 192 | F8C0EAF6168C986000209C5D /* CYAppDelegate.m in Sources */, 193 | F8C0EAF7168C986000209C5D /* CYViewController.m in Sources */, 194 | F8C0EB07168C989000209C5D /* main.m in Sources */, 195 | ); 196 | runOnlyForDeploymentPostprocessing = 0; 197 | }; 198 | /* End PBXSourcesBuildPhase section */ 199 | 200 | /* Begin PBXVariantGroup section */ 201 | F8C0EAF8168C987700209C5D /* CYViewController.xib */ = { 202 | isa = PBXVariantGroup; 203 | children = ( 204 | F8C0EAF9168C987700209C5D /* en */, 205 | ); 206 | name = CYViewController.xib; 207 | sourceTree = ""; 208 | }; 209 | F8C0EAFB168C987E00209C5D /* InfoPlist.strings */ = { 210 | isa = PBXVariantGroup; 211 | children = ( 212 | F8C0EAFC168C987E00209C5D /* en */, 213 | ); 214 | name = InfoPlist.strings; 215 | sourceTree = ""; 216 | }; 217 | /* End PBXVariantGroup section */ 218 | 219 | /* Begin XCBuildConfiguration section */ 220 | F8C0EADB168B941A00209C5D /* Debug */ = { 221 | isa = XCBuildConfiguration; 222 | buildSettings = { 223 | ALWAYS_SEARCH_USER_PATHS = NO; 224 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 225 | CLANG_CXX_LIBRARY = "libc++"; 226 | CLANG_ENABLE_OBJC_ARC = YES; 227 | CLANG_WARN_EMPTY_BODY = YES; 228 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 229 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 230 | COPY_PHASE_STRIP = NO; 231 | GCC_C_LANGUAGE_STANDARD = gnu99; 232 | GCC_DYNAMIC_NO_PIC = NO; 233 | GCC_OPTIMIZATION_LEVEL = 0; 234 | GCC_PREPROCESSOR_DEFINITIONS = ( 235 | "DEBUG=1", 236 | "$(inherited)", 237 | ); 238 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 239 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 240 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 241 | GCC_WARN_UNUSED_VARIABLE = YES; 242 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 243 | ONLY_ACTIVE_ARCH = YES; 244 | SDKROOT = iphoneos; 245 | }; 246 | name = Debug; 247 | }; 248 | F8C0EADC168B941A00209C5D /* Release */ = { 249 | isa = XCBuildConfiguration; 250 | buildSettings = { 251 | ALWAYS_SEARCH_USER_PATHS = NO; 252 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 253 | CLANG_CXX_LIBRARY = "libc++"; 254 | CLANG_ENABLE_OBJC_ARC = YES; 255 | CLANG_WARN_EMPTY_BODY = YES; 256 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 257 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 258 | COPY_PHASE_STRIP = YES; 259 | GCC_C_LANGUAGE_STANDARD = gnu99; 260 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 261 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 262 | GCC_WARN_UNUSED_VARIABLE = YES; 263 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 264 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 265 | SDKROOT = iphoneos; 266 | VALIDATE_PRODUCT = YES; 267 | }; 268 | name = Release; 269 | }; 270 | F8C0EADE168B941A00209C5D /* Debug */ = { 271 | isa = XCBuildConfiguration; 272 | buildSettings = { 273 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 274 | GCC_PREFIX_HEADER = "ShotBlockerExample/ShotBlocker-Prefix.pch"; 275 | INFOPLIST_FILE = "ShotBlockerExample/ShotBlocker-Info.plist"; 276 | PRODUCT_NAME = "$(TARGET_NAME)"; 277 | WRAPPER_EXTENSION = app; 278 | }; 279 | name = Debug; 280 | }; 281 | F8C0EADF168B941A00209C5D /* Release */ = { 282 | isa = XCBuildConfiguration; 283 | buildSettings = { 284 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 285 | GCC_PREFIX_HEADER = "ShotBlockerExample/ShotBlocker-Prefix.pch"; 286 | INFOPLIST_FILE = "ShotBlockerExample/ShotBlocker-Info.plist"; 287 | PRODUCT_NAME = "$(TARGET_NAME)"; 288 | WRAPPER_EXTENSION = app; 289 | }; 290 | name = Release; 291 | }; 292 | /* End XCBuildConfiguration section */ 293 | 294 | /* Begin XCConfigurationList section */ 295 | F8C0EAB3168B941A00209C5D /* Build configuration list for PBXProject "ShotBlocker" */ = { 296 | isa = XCConfigurationList; 297 | buildConfigurations = ( 298 | F8C0EADB168B941A00209C5D /* Debug */, 299 | F8C0EADC168B941A00209C5D /* Release */, 300 | ); 301 | defaultConfigurationIsVisible = 0; 302 | defaultConfigurationName = Release; 303 | }; 304 | F8C0EADD168B941A00209C5D /* Build configuration list for PBXNativeTarget "ShotBlocker" */ = { 305 | isa = XCConfigurationList; 306 | buildConfigurations = ( 307 | F8C0EADE168B941A00209C5D /* Debug */, 308 | F8C0EADF168B941A00209C5D /* Release */, 309 | ); 310 | defaultConfigurationIsVisible = 0; 311 | }; 312 | /* End XCConfigurationList section */ 313 | }; 314 | rootObject = F8C0EAB0168B941A00209C5D /* Project object */; 315 | } 316 | -------------------------------------------------------------------------------- /ShotBlocker.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ShotBlocker.xcodeproj/project.xcworkspace/xcuserdata/clayallsopp.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clayallsopp/ShotBlocker/f8852c9b9244db0ae22e9998d016ff41558d6120/ShotBlocker.xcodeproj/project.xcworkspace/xcuserdata/clayallsopp.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /ShotBlocker.xcodeproj/xcuserdata/clayallsopp.xcuserdatad/xcdebugger/Breakpoints.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /ShotBlocker.xcodeproj/xcuserdata/clayallsopp.xcuserdatad/xcschemes/ShotBlocker.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 51 | 52 | 58 | 59 | 60 | 61 | 62 | 63 | 69 | 70 | 76 | 77 | 78 | 79 | 81 | 82 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /ShotBlocker.xcodeproj/xcuserdata/clayallsopp.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ShotBlocker.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | F8C0EAB8168B941A00209C5D 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /ShotBlocker/ShotBlocker.h: -------------------------------------------------------------------------------- 1 | // 2 | // ShotBlocker.h 3 | // ShotBlocker 4 | // 5 | // Created by Clay Allsopp on 12/26/12. 6 | // Copyright (c) 2012 Clay Allsopp. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef void (^ShotBlockerScreenshotBlock)(); 12 | typedef void (^ShotBlockerScreenshotImageBlock)(UIImage *screenshot); 13 | typedef void (^ShotBlockerScreenshotErrorBlock)(NSError *error); 14 | 15 | @interface ShotBlocker : NSObject 16 | 17 | + (ShotBlocker *)sharedManager; 18 | 19 | // Callbacks run on the main thread. 20 | - (void)detectScreenshotWithBlock:(ShotBlockerScreenshotBlock)block; 21 | - (void)detectScreenshotWithBlock:(ShotBlockerScreenshotBlock)block andErrorBlock:(ShotBlockerScreenshotErrorBlock)errorBlock; 22 | - (void)detectScreenshotWithImageBlock:(ShotBlockerScreenshotImageBlock)block; 23 | - (void)detectScreenshotWithImageBlock:(ShotBlockerScreenshotImageBlock)block andErrorBlock:(ShotBlockerScreenshotErrorBlock)errorBlock; 24 | ; 25 | 26 | - (void)stopDetectingScreenshots; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /ShotBlocker/ShotBlocker.m: -------------------------------------------------------------------------------- 1 | // 2 | // ShotBlocker.m 3 | // ShotBlocker 4 | // 5 | // Created by Clay Allsopp on 12/26/12. 6 | // Copyright (c) 2012 Clay Allsopp. All rights reserved. 7 | // 8 | 9 | #import "ShotBlocker.h" 10 | #import 11 | 12 | static NSTimeInterval const kShotBlockerUpdateInterval = 1.0; 13 | 14 | @interface ShotBlocker() { 15 | dispatch_source_t _timer; 16 | } 17 | @property (readwrite, nonatomic, strong) NSMutableDictionary *groupCounts; 18 | @property (readwrite, nonatomic, copy) ShotBlockerScreenshotBlock screenshotBlock; 19 | @property (readwrite, nonatomic, copy) ShotBlockerScreenshotImageBlock imageBlock; 20 | @property (readwrite, nonatomic, copy) ShotBlockerScreenshotErrorBlock errorBlock; 21 | 22 | + (ALAssetsLibrary *)assetsLibrary; 23 | 24 | - (void)startTimer; 25 | - (void)checkForNewScreenshot; 26 | + (BOOL)isScreenshot:(UIImage *)image; 27 | @end 28 | 29 | @implementation ShotBlocker 30 | @synthesize groupCounts = _groupCounts; 31 | @synthesize screenshotBlock = _screenshotBlock; 32 | @synthesize imageBlock = _imageBlock; 33 | @synthesize errorBlock = _errorBlock; 34 | 35 | + (ShotBlocker *)sharedManager { 36 | static ShotBlocker *_sharedManager = nil; 37 | static dispatch_once_t oncePredicate; 38 | dispatch_once(&oncePredicate, ^{ 39 | _sharedManager = [[self alloc] init]; 40 | }); 41 | 42 | return _sharedManager; 43 | } 44 | 45 | + (ALAssetsLibrary *)assetsLibrary { 46 | static dispatch_once_t oncePredicate; 47 | static ALAssetsLibrary *_library = nil; 48 | dispatch_once(&oncePredicate, ^{ 49 | _library = [[ALAssetsLibrary alloc] init]; 50 | }); 51 | return _library; 52 | } 53 | 54 | - (void)detectScreenshotWithBlock:(ShotBlockerScreenshotBlock)block { 55 | self.screenshotBlock = block; 56 | 57 | [self startTimer]; 58 | } 59 | 60 | - (void)detectScreenshotWithBlock:(ShotBlockerScreenshotBlock)block andErrorBlock:(ShotBlockerScreenshotErrorBlock)errorBlock { 61 | self.screenshotBlock = block; 62 | self.errorBlock = errorBlock; 63 | 64 | [self startTimer]; 65 | } 66 | 67 | - (void)detectScreenshotWithImageBlock:(ShotBlockerScreenshotImageBlock)block { 68 | self.imageBlock = block; 69 | 70 | [self startTimer]; 71 | } 72 | 73 | - (void)detectScreenshotWithImageBlock:(ShotBlockerScreenshotImageBlock)block andErrorBlock:(ShotBlockerScreenshotErrorBlock)errorBlock { 74 | self.imageBlock = block; 75 | self.errorBlock = errorBlock; 76 | 77 | [self startTimer]; 78 | } 79 | 80 | - (void)stopDetectingScreenshots { 81 | if (_timer) { 82 | dispatch_suspend(_timer); 83 | dispatch_resume(_timer); 84 | _timer = nil; 85 | } 86 | self.groupCounts = nil; 87 | self.screenshotBlock = nil; 88 | self.imageBlock = nil; 89 | self.errorBlock = nil; 90 | } 91 | 92 | - (void)startTimer { 93 | uint64_t interval = kShotBlockerUpdateInterval * NSEC_PER_SEC; 94 | uint64_t leeway = 0.3 * interval; // 30% tolerance 95 | dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 96 | self.groupCounts = [NSMutableDictionary dictionary]; 97 | 98 | if (_timer) { 99 | [self stopDetectingScreenshots]; 100 | } 101 | 102 | _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); 103 | if (_timer) 104 | { 105 | dispatch_source_set_timer(_timer, dispatch_walltime(NULL, 0), interval, leeway); 106 | dispatch_source_set_event_handler(_timer, ^(){ 107 | [self checkForNewScreenshot]; 108 | }); 109 | dispatch_resume(_timer); 110 | } 111 | } 112 | 113 | - (void)checkForNewScreenshot { 114 | ALAssetsLibrary *library = [[self class] assetsLibrary]; 115 | 116 | [library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop) { 117 | 118 | if (group) { 119 | NSString *groupName = (NSString *)[group valueForProperty:ALAssetsGroupPropertyPersistentID]; 120 | [group setAssetsFilter:[ALAssetsFilter allPhotos]]; 121 | 122 | if ([self.groupCounts objectForKey:groupName] == nil) { 123 | [self.groupCounts setObject:[NSNumber numberWithInt:group.numberOfAssets] forKey:groupName]; 124 | } 125 | 126 | if (group.numberOfAssets > [[self.groupCounts objectForKey:groupName] intValue]) { 127 | // Chooses the photo at the last index 128 | [group enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndex:([group numberOfAssets] - 1)] options:0 usingBlock:^(ALAsset *alAsset, NSUInteger index, BOOL *innerStop) { 129 | 130 | // The end of the enumeration is signaled by asset == nil. 131 | if (alAsset) { 132 | ALAssetRepresentation *representation = [alAsset defaultRepresentation]; 133 | UIImage *latestPhoto = [UIImage imageWithCGImage:[representation fullScreenImage]]; 134 | 135 | if ([[self class] isScreenshot:latestPhoto]) { 136 | dispatch_async(dispatch_get_main_queue(), ^() { 137 | if (self.screenshotBlock) { 138 | self.screenshotBlock(); 139 | } 140 | if (self.imageBlock) { 141 | self.imageBlock(latestPhoto); 142 | } 143 | }); 144 | } 145 | 146 | [self.groupCounts setObject:[NSNumber numberWithInt:group.numberOfAssets] forKey:groupName]; 147 | } 148 | }]; 149 | } 150 | } 151 | } failureBlock: ^(NSError *error) { 152 | if (self.errorBlock) { 153 | dispatch_async(dispatch_get_main_queue(), ^() { 154 | self.errorBlock(error); 155 | }); 156 | } 157 | else { 158 | NSLog(@"Failed to access ALAssetsLibrary %@ with error: %@", library, error.localizedDescription); 159 | } 160 | 161 | [self stopDetectingScreenshots]; 162 | }]; 163 | } 164 | 165 | + (BOOL)isScreenshot:(UIImage *)image { 166 | CGFloat imageWidth = image.size.width; 167 | CGFloat imageHeight = image.size.height; 168 | CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width; 169 | CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height; 170 | // imageWidth/Height is in points; screenWidth/Height is in pixels 171 | // so on a retina device, screenWidth/Height is 2x. 172 | // fmodf takes care of the scale factor for us, so we just compare 173 | // widths and heights to see if either orientation matches. 174 | return (fmodf(imageWidth, screenWidth) == 0 && fmodf(imageHeight, screenHeight) == 0) || 175 | (fmodf(imageWidth, screenHeight) == 0 && fmodf(imageHeight, screenWidth) == 0); 176 | } 177 | 178 | @end 179 | -------------------------------------------------------------------------------- /ShotBlockerExample/CYAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // CYAppDelegate.h 3 | // ShotBlocker 4 | // 5 | // Created by Clay Allsopp on 12/26/12. 6 | // Copyright (c) 2012 Clay Allsopp. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class CYViewController; 12 | 13 | @interface CYAppDelegate : UIResponder 14 | 15 | @property (strong, nonatomic) UIWindow *window; 16 | 17 | @property (strong, nonatomic) CYViewController *viewController; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /ShotBlockerExample/CYAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // CYAppDelegate.m 3 | // ShotBlocker 4 | // 5 | // Created by Clay Allsopp on 12/26/12. 6 | // Copyright (c) 2012 Clay Allsopp. All rights reserved. 7 | // 8 | 9 | #import "CYAppDelegate.h" 10 | 11 | #import "CYViewController.h" 12 | 13 | #import "ShotBlocker.h" 14 | 15 | @implementation CYAppDelegate 16 | 17 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 18 | { 19 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 20 | // Override point for customization after application launch. 21 | self.viewController = [[CYViewController alloc] initWithNibName:@"CYViewController" bundle:nil]; 22 | self.window.rootViewController = self.viewController; 23 | [self.window makeKeyAndVisible]; 24 | 25 | return YES; 26 | } 27 | 28 | - (void)applicationWillResignActive:(UIApplication *)application 29 | { 30 | // 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. 31 | // 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. 32 | } 33 | 34 | - (void)applicationDidEnterBackground:(UIApplication *)application 35 | { 36 | // 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. 37 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 38 | } 39 | 40 | - (void)applicationWillEnterForeground:(UIApplication *)application 41 | { 42 | // 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. 43 | } 44 | 45 | - (void)applicationDidBecomeActive:(UIApplication *)application 46 | { 47 | // 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. 48 | } 49 | 50 | - (void)applicationWillTerminate:(UIApplication *)application 51 | { 52 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 53 | } 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /ShotBlockerExample/CYViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // CYViewController.h 3 | // ShotBlocker 4 | // 5 | // Created by Clay Allsopp on 12/26/12. 6 | // Copyright (c) 2012 Clay Allsopp. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CYViewController : UIViewController 12 | 13 | @property (nonatomic, strong) IBOutlet UIButton *stopButton; 14 | @property (nonatomic, strong) IBOutlet UIImageView *imageView; 15 | 16 | - (IBAction)startWatch:(id)sender; 17 | - (IBAction)endWatch:(id)sender; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /ShotBlockerExample/CYViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // CYViewController.m 3 | // ShotBlocker 4 | // 5 | // Created by Clay Allsopp on 12/26/12. 6 | // Copyright (c) 2012 Clay Allsopp. All rights reserved. 7 | // 8 | 9 | #import "CYViewController.h" 10 | 11 | #import "ShotBlocker.h" 12 | 13 | @interface CYViewController () 14 | 15 | @end 16 | 17 | @implementation CYViewController 18 | @synthesize imageView=_imageView; 19 | @synthesize stopButton=_stopButton; 20 | 21 | - (void)viewDidLoad { 22 | [super viewDidLoad]; 23 | 24 | self.imageView.contentMode = UIViewContentModeScaleAspectFit; 25 | self.imageView.backgroundColor = UIColor.whiteColor; 26 | self.stopButton.enabled = NO; 27 | } 28 | 29 | - (IBAction)startWatch:(id)sender { 30 | NSLog(@"Starting..."); 31 | [[ShotBlocker sharedManager] detectScreenshotWithImageBlock:^(UIImage *screenshot) { 32 | NSLog(@"Screenshot! %@", screenshot); 33 | self.imageView.image = screenshot; 34 | [self.imageView setNeedsDisplay]; 35 | }]; 36 | self.stopButton.enabled = YES; 37 | } 38 | 39 | - (IBAction)endWatch:(id)sender { 40 | NSLog(@"Ending..."); 41 | [[ShotBlocker sharedManager] stopDetectingScreenshots]; 42 | self.stopButton.enabled = NO; 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /ShotBlockerExample/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clayallsopp/ShotBlocker/f8852c9b9244db0ae22e9998d016ff41558d6120/ShotBlockerExample/Default-568h@2x.png -------------------------------------------------------------------------------- /ShotBlockerExample/Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clayallsopp/ShotBlocker/f8852c9b9244db0ae22e9998d016ff41558d6120/ShotBlockerExample/Default.png -------------------------------------------------------------------------------- /ShotBlockerExample/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clayallsopp/ShotBlocker/f8852c9b9244db0ae22e9998d016ff41558d6120/ShotBlockerExample/Default@2x.png -------------------------------------------------------------------------------- /ShotBlockerExample/ShotBlocker-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.clay.apptory.lib.${PRODUCT_NAME:rfc1034identifier} 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 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /ShotBlockerExample/ShotBlocker-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'ShotBlocker' target in the 'ShotBlocker' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_4_0 8 | #warning "This project uses features only available in iOS SDK 4.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /ShotBlockerExample/en.lproj/CYViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1536 5 | 12C60 6 | 2844 7 | 1187.34 8 | 625.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 1930 12 | 13 | 14 | IBNSLayoutConstraint 15 | IBProxyObject 16 | IBUIButton 17 | IBUIImageView 18 | IBUIView 19 | 20 | 21 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 22 | 23 | 24 | PluginDependencyRecalculationVersion 25 | 26 | 27 | 28 | 29 | IBFilesOwner 30 | IBCocoaTouchFramework 31 | 32 | 33 | IBFirstResponder 34 | IBCocoaTouchFramework 35 | 36 | 37 | 38 | 274 39 | 40 | 41 | 42 | 292 43 | {{40, 59}, {116, 44}} 44 | 45 | 46 | 47 | _NS:9 48 | NO 49 | IBCocoaTouchFramework 50 | 0 51 | 0 52 | 1 53 | Watch 54 | 55 | 3 56 | MQA 57 | 58 | 59 | 1 60 | MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA 61 | 62 | 63 | 3 64 | MC41AA 65 | 66 | 67 | 2 68 | 15 69 | 70 | 71 | Helvetica-Bold 72 | 15 73 | 16 74 | 75 | 76 | 77 | 78 | 292 79 | {{40, 117}, {240, 259}} 80 | 81 | 82 | _NS:9 83 | NO 84 | IBCocoaTouchFramework 85 | 86 | 87 | 88 | 292 89 | {{164, 59}, {116, 44}} 90 | 91 | 92 | 93 | _NS:9 94 | NO 95 | IBCocoaTouchFramework 96 | NO 97 | 0 98 | 0 99 | 1 100 | Stop 101 | 102 | 103 | 1 104 | MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA 105 | 106 | 107 | 108 | 109 | 110 | 111 | {{0, 20}, {320, 548}} 112 | 113 | 114 | 115 | 116 | 3 117 | MC43NQA 118 | 119 | 2 120 | 121 | 122 | NO 123 | 124 | 125 | IBUIScreenMetrics 126 | 127 | YES 128 | 129 | 130 | 131 | 132 | 133 | {320, 568} 134 | {568, 320} 135 | 136 | 137 | IBCocoaTouchFramework 138 | Retina 4 Full Screen 139 | 2 140 | 141 | IBCocoaTouchFramework 142 | 143 | 144 | 145 | 146 | 147 | 148 | view 149 | 150 | 151 | 152 | 7 153 | 154 | 155 | 156 | stopButton 157 | 158 | 159 | 160 | 43 161 | 162 | 163 | 164 | imageView 165 | 166 | 167 | 168 | 46 169 | 170 | 171 | 172 | startWatch: 173 | 174 | 175 | 7 176 | 177 | 44 178 | 179 | 180 | 181 | endWatch: 182 | 183 | 184 | 7 185 | 186 | 45 187 | 188 | 189 | 190 | 191 | 192 | 0 193 | 194 | 195 | 196 | 197 | 198 | -1 199 | 200 | 201 | File's Owner 202 | 203 | 204 | -2 205 | 206 | 207 | 208 | 209 | 6 210 | 211 | 212 | 213 | 214 | 6 215 | 0 216 | 217 | 6 218 | 1 219 | 220 | 0.0 221 | 222 | 1000 223 | 224 | 6 225 | 24 226 | 2 227 | 228 | 229 | 230 | 5 231 | 0 232 | 233 | 6 234 | 1 235 | 236 | 8 237 | 238 | 1000 239 | 240 | 6 241 | 24 242 | 3 243 | 244 | 245 | 246 | 11 247 | 0 248 | 249 | 11 250 | 1 251 | 252 | 0.0 253 | 254 | 1000 255 | 256 | 6 257 | 24 258 | 2 259 | 260 | 261 | 262 | 9 263 | 0 264 | 265 | 9 266 | 1 267 | 268 | 0.0 269 | 270 | 1000 271 | 272 | 5 273 | 22 274 | 2 275 | 276 | 277 | 278 | 3 279 | 0 280 | 281 | 3 282 | 1 283 | 284 | 117 285 | 286 | 1000 287 | 288 | 3 289 | 9 290 | 3 291 | 292 | 293 | 294 | 5 295 | 0 296 | 297 | 5 298 | 1 299 | 300 | 0.0 301 | 302 | 1000 303 | 304 | 6 305 | 24 306 | 2 307 | 308 | 309 | 310 | 3 311 | 0 312 | 313 | 3 314 | 1 315 | 316 | 59 317 | 318 | 1000 319 | 320 | 3 321 | 9 322 | 3 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 8 332 | 333 | 334 | 335 | 336 | 7 337 | 0 338 | 339 | 0 340 | 1 341 | 342 | 116 343 | 344 | 1000 345 | 346 | 3 347 | 9 348 | 1 349 | 350 | 351 | 352 | 353 | 354 | 11 355 | 356 | 357 | 358 | 359 | 8 360 | 0 361 | 362 | 0 363 | 1 364 | 365 | 259 366 | 367 | 1000 368 | 369 | 3 370 | 9 371 | 1 372 | 373 | 374 | 375 | 376 | 377 | 15 378 | 379 | 380 | 381 | 382 | 16 383 | 384 | 385 | 386 | 387 | 17 388 | 389 | 390 | 391 | 392 | 7 393 | 0 394 | 395 | 0 396 | 1 397 | 398 | 116 399 | 400 | 1000 401 | 402 | 3 403 | 9 404 | 1 405 | 406 | 407 | 408 | 409 | 410 | 23 411 | 412 | 413 | 414 | 415 | 26 416 | 417 | 418 | 419 | 420 | 28 421 | 422 | 423 | 424 | 425 | 34 426 | 427 | 428 | 429 | 430 | 37 431 | 432 | 433 | 434 | 435 | 39 436 | 437 | 438 | 439 | 440 | 41 441 | 442 | 443 | 444 | 445 | 42 446 | 447 | 448 | 449 | 450 | 451 | 452 | CYViewController 453 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 454 | UIResponder 455 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 456 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 457 | 458 | 459 | 460 | 461 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 462 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 463 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 464 | 465 | 466 | 467 | 468 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 469 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 470 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 471 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 472 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 473 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 474 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 475 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 476 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 46 497 | 498 | 499 | 500 | 501 | CYViewController 502 | UIViewController 503 | 504 | id 505 | id 506 | id 507 | 508 | 509 | 510 | endWatch: 511 | id 512 | 513 | 514 | startWatch: 515 | id 516 | 517 | 518 | stopWatch: 519 | id 520 | 521 | 522 | 523 | UIImageView 524 | UIButton 525 | 526 | 527 | 528 | imageView 529 | UIImageView 530 | 531 | 532 | stopButton 533 | UIButton 534 | 535 | 536 | 537 | IBProjectSource 538 | ./Classes/CYViewController.h 539 | 540 | 541 | 542 | NSLayoutConstraint 543 | NSObject 544 | 545 | IBProjectSource 546 | ./Classes/NSLayoutConstraint.h 547 | 548 | 549 | 550 | 551 | 0 552 | IBCocoaTouchFramework 553 | YES 554 | 3 555 | YES 556 | 1930 557 | 558 | 559 | -------------------------------------------------------------------------------- /ShotBlockerExample/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /ShotBlockerExample/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // ShotBlocker 4 | // 5 | // Created by Clay Allsopp on 12/26/12. 6 | // Copyright (c) 2012 Clay Allsopp. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "CYAppDelegate.h" 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([CYAppDelegate class])); 17 | } 18 | } 19 | --------------------------------------------------------------------------------