Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>This is how I have done it, it is a bit complicated but I will try and take you through it step by step:</p> <hr> <p>Before we start, in your <strong>header file</strong> declare the following variables and methods : </p> <pre><code>BOOL shouldObserveDesktop; NSDictionary *knownScreenshotsOnDesktop; NSString *screenshotLocation; NSString *screenshotFilenameSuffix; - (void)startObservingDesktop; - (void)stopObservingDesktop; - (NSDictionary *)screenshotsOnDesktop; - (NSDictionary *)screenshotsAtPath:(NSString *)dirpath modifiedAfterDate:(NSDate *)lmod; - (void)checkForScreenshotsAtPath:(NSString *)dirpath; - (NSDictionary *)findUnprocessedScreenshotsOnDesktop; </code></pre> <p>Now in your <strong>implementation file</strong>, firstly add this code: </p> <pre><code>- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { screenshotLocation = [[NSString stringWithString:@"~/Desktop"] retain]; screenshotFilenameSuffix = [[NSString stringWithString:@".png"] retain]; knownScreenshotsOnDesktop = [[self screenshotsOnDesktop] retain]; [self startObservingDesktop]; } </code></pre> <p>This sets up the variables up for when all the methods are called. Next add:</p> <pre><code>- (void)onDirectoryNotification:(NSNotification *)n { id obj = [n object]; if (obj &amp;&amp; [obj isKindOfClass:[NSString class]]) { [self checkForScreenshotsAtPath:screenshotLocation]; } } - (void)startObservingDesktop { if (shouldObserveDesktop) return; NSDistributedNotificationCenter *dnc = [NSDistributedNotificationCenter defaultCenter]; [dnc addObserver:self selector:@selector(onDirectoryNotification:) name:@"com.apple.carbon.core.DirectoryNotification" object:nil suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately]; shouldObserveDesktop = YES; } - (void)stopObservingDesktop { if (!shouldObserveDesktop) return; NSDistributedNotificationCenter *dnc = [NSDistributedNotificationCenter defaultCenter]; [dnc removeObserver:self name:@"com.apple.carbon.core.DirectoryNotification" object:nil]; shouldObserveDesktop = NO; } </code></pre> <p>Here we observe the notification that will be called when a screenshot is taken and pass it the method to call (in this case <code>onDirectoryNotification:</code>). There is also the method to stop observing the desktop/notification. The notification calls <code>checkForScreenshotsAtPath:</code> which will check for screenshots on the desktop. The following is the code for that method and the other methods that it calls:</p> <pre><code>-(void)checkForScreenshotsAtPath:(NSString *)dirpath { NSDictionary *files; NSArray *paths; // find new screenshots if (!(files = [self findUnprocessedScreenshotsOnDesktop])) return; // sort on key (path) paths = [files keysSortedByValueUsingComparator:^(id a, id b) { return [b compare:a]; }]; // process each file for (NSString *path in paths) { // Process the file at the path } } -(NSDictionary *)findUnprocessedScreenshotsOnDesktop { NSDictionary *currentFiles; NSMutableDictionary *files; NSMutableSet *newFilenames; currentFiles = [self screenshotsOnDesktop]; files = nil; if ([currentFiles count]) { newFilenames = [NSMutableSet setWithArray:[currentFiles allKeys]]; // filter: remove allready processed screenshots [newFilenames minusSet:[NSSet setWithArray:[knownScreenshotsOnDesktop allKeys]]]; if ([newFilenames count]) { files = [NSMutableDictionary dictionaryWithCapacity:1]; for (NSString *path in newFilenames) { [files setObject:[currentFiles objectForKey:path] forKey:path]; } } } knownScreenshotsOnDesktop = currentFiles; return files; } -(NSDictionary *)screenshotsOnDesktop { NSDate *lmod = [NSDate dateWithTimeIntervalSinceNow:-5]; // max 5 sec old return [self screenshotsAtPath:screenshotLocation modifiedAfterDate:lmod]; } </code></pre> <p>They were the first 3 methods that the notification in turn calls and the following code is the final method <code>screenshotsAtPath:modifiedAfterDate:</code> which I will warn you is extremely long as it has to confirm that the file is definitely a screenshot:</p> <pre><code>-(NSDictionary *)screenshotsAtPath:(NSString *)dirpath modifiedAfterDate:(NSDate *)lmod { NSFileManager *fm = [NSFileManager defaultManager]; NSArray *direntries; NSMutableDictionary *files = [NSMutableDictionary dictionary]; NSString *path; NSDate *mod; NSError *error; NSDictionary *attrs; dirpath = [dirpath stringByExpandingTildeInPath]; direntries = [fm contentsOfDirectoryAtPath:dirpath error:&amp;error]; if (!direntries) { return nil; } for (NSString *fn in direntries) { // always skip dotfiles if ([fn hasPrefix:@"."]) { //[log debug:@"%s skipping: filename begins with a dot", _cmd]; continue; } // skip any file not ending in screenshotFilenameSuffix (".png" by default) if (([fn length] &lt; 10) || // ".png" suffix is expected (![fn compare:screenshotFilenameSuffix options:NSCaseInsensitiveSearch range:NSMakeRange([fn length]-5, 4)] != NSOrderedSame) ) { continue; } // build path path = [dirpath stringByAppendingPathComponent:fn]; // Skip any file which name does not contain a space. // You want to avoid matching the filename against // all possible screenshot file name schemas (must be hundreds), we make the // assumption that all language formats have this in common: it contains at least one space. if ([fn rangeOfString:@" "].location == NSNotFound) { continue; } // query file attributes (rich stat) attrs = [fm attributesOfItemAtPath:path error:&amp;error]; if (!attrs) { continue; } // must be a regular file if ([attrs objectForKey:NSFileType] != NSFileTypeRegular) { continue; } // check last modified date mod = [attrs objectForKey:NSFileModificationDate]; if (lmod &amp;&amp; (!mod || [mod compare:lmod] == NSOrderedAscending)) { // file is too old continue; } // find key for NSFileExtendedAttributes NSString *xattrsKey = nil; for (NSString *k in [attrs keyEnumerator]) { if ([k isEqualToString:@"NSFileExtendedAttributes"]) { xattrsKey = k; break; } } if (!xattrsKey) { // no xattrs continue; } NSDictionary *xattrs = [attrs objectForKey:xattrsKey]; if (!xattrs || ![xattrs objectForKey:@"com.apple.metadata:kMDItemIsScreenCapture"]) { continue; } // ok, let's use this file [files setObject:mod forKey:path]; } return files; } </code></pre> <hr> <p>Well, there you have it. That's how I was able to detect when the user takes a screenshot, it probably has a few bugs but it seems to work fine at the moment. If you want all the code in one here are the links for it at pastebin.com:</p> <p><strong>Header</strong> - <a href="http://pastebin.com/gBAbCBJB" rel="nofollow">http://pastebin.com/gBAbCBJB</a></p> <p><strong>Implementation</strong> - <a href="http://pastebin.com/VjQ6P3zQ" rel="nofollow">http://pastebin.com/VjQ6P3zQ</a></p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload