2010-12-23 23 views
10

Kullanıcı bir ekran görüntüsü aldığında veya Command-Shift-4 aracılığıyla bir bildirim almam için bir uygulama arıyorum.Bir Kullanıcı bir ekran görüntüsünü aldığında algılar

Bunun bir örneği, alınan görüntüyü otomatik olarak yükleyen Droplr ve Cloud App gibi uygulamalardır.

Çevreyi araştırıyorum ve bunun Darwin Bildirimleri ile ilgili bir şey olabileceğini öğrendim ama nereden başlayacağımı bilmiyorum. Bir kullanıcı bir ekran yüzden

vuruldu aldığında

+0

bu benzer soruya bir göz atın: http://stackoverflow.com/questions/2815502/ otomatik-ekran görüntüsü-mac-mac-like-bulut-app, iyi bir cevabı var gibi görünüyor –

+0

Bu iyi bir cevap var ve ben bir göz atacağım. Teşekkürler. – Joshua

+0

Sadece cevabımı şu adrese gönderdim: http://stackoverflow.com/questions/4516852/detect-when-a-user-takes-a-screenshot/4519189#4519189. – Joshua

cevap

14

Bu daha önceki yorumların birinde sözü edilen, ancak nereye kMDItemIsScreenCapture = 1 dosyaları aramaya bir NSMetadataQuery kullanabilirsiniz. Bu, ekran görüntüsü dosyalarına eklenen özel bir özelliktir.

Sadece bunu nasıl gösteren küçük bir demo kadar çırpılmış ve github üzerinde yayınladı:

https://github.com/davedelong/Demos/blob/master/ScreenShot%20Detector

+0

Haha. Vay, yaptığımdan çok daha basit, bunu yapmak için çok teşekkürler! – Joshua

+0

@Joshua evet, aslında ekran görüntülerini bulmak oldukça basit. Ui'ye gelince, bağlamalar tamamen kodsuz hale getirdi. :) sadece bir çift basit değer transformatörü ile resim görünümü ve pathcontrol ile çalışmak. –

+0

Bağlantılar daha da şaşırtıcıydı. Bağlamaların çok güçlü olabileceğini hiç düşünmemiştim ve çok fazla kod satırı kurtarabilirdim! – Joshua

-3

sen sistem bildirim gönderilir, bir nesneyi kayıt vardır:

[[NSNotificationCenter defaultCenter] addObserver: theObjectToRecieveTheNotification selector:@selector(theMethodToPerformWhenNotificationIsRecieved) name:@"theNameOftheScreenCapturedNotification" object: optionallyAnObjectOrArgumentThatIsPassedToTheMethodToBecalled]; 

değil bildirim adı ne olduğundan emin ama dışarısı muhtemelen.

dealloc kendinizi de kaydını unutmayın:

[[NSNotificationCenter defaultCenter] removeObserver: kendini];

+3

Zaten bunu nasıl yapacağımı biliyorum ama sorun hangi bildirimlerin gözlemleneceğini bilmek. Bildiğim kadarıyla, bir kullanıcının ekran görüntüsünü aldığı zamanlar yoktur. – Joshua

+0

Böyle bir bildirim de bulamıyorum. –

3

Bu benim yapmış nasıl, biraz karmaşık ama adım adım yoluyla denemek ve götürecektir: Başlamadan önce


, sizin başlık dosyasına beyan aşağıdaki değişkenler ve yöntem:

:

sizin uygulama dosyasında Şimdi
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; 

, öncelikle bu kodu ekleyin

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { 
    screenshotLocation = [[NSString stringWithString:@"~/Desktop"] retain]; 
    screenshotFilenameSuffix = [[NSString stringWithString:@".png"] retain]; 
    knownScreenshotsOnDesktop = [[self screenshotsOnDesktop] retain]; 
    [self startObservingDesktop]; 
} 

Bu, tüm yöntemlerin çağrıldığı zamanlar için değişkenleri ayarlar. Sonraki eklenti:

- (void)onDirectoryNotification:(NSNotification *)n { 
    id obj = [n object]; 
    if (obj && [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; 
} 

Burada bir ekran görüntüsü çekildiğinde denilen ve (bu durumda onDirectoryNotification: olarak) çağırmak için o yöntemi geçmesi edilecek bildirim gözlemliyoruz. Masaüstü/bildirimi gözlemlemeyi durdurma yöntemi de vardır. Bildirim, masaüstünde ekran görüntülerini kontrol edecek olan checkForScreenshotsAtPath:'u arar. Aşağıdaki bu yöntemin kodu ve çağrıları diğer yöntemler ise:

Onlar dönüş çağrıları bildirimi ve aşağıdaki kod nihai yöntem sana olan uyarır screenshotsAtPath:modifiedAfterDate: ilk 3 yöntemleri vardı
-(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]; 
} 

son derece uzun sahip olduğu gibi dosya kesinlikle bir ekran görüntüsü olduğunu doğrulamak için:

-(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:&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] < 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:&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 && (!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; 
} 

Eh, orada o var. Bu, kullanıcının bir ekran görüntüsü aldığında bunu nasıl algılayabildiğimi, muhtemelen birkaç hataya sahip olduğunu, ancak şu anda gayet iyi çalışıyor gibi görünebildi. Birindeki tüm kodun burada olmasını istiyorsanız, bunun pastebin'deki bağlantıları.com:

Başlık - http://pastebin.com/gBAbCBJB

Uygulama - http://pastebin.com/VjQ6P3zQ

+1

Sadece "kMDItemIsScreenCapture == 1" için yeni eşleşmeleri izlemek için bir NSMetadataQuery/MDQuery çalışmasını sürdürmek daha kolay olmaz mıydı? –

+0

Hmm… Buna rağmen, bir bakacağım. Teşekkürler! – Joshua

+0

Ayrıca, kullanıcının ekran görüntülerinin kaydedildiği konumu değiştirebileceğini unutmayın. Her zaman masaüstünde olmayacak. –

İlgili konular