Mac OS?-Finder Sync使用入門

1. 初識Finder Sync

借助Finder Sync擴展,您可以注冊一個或多個文件夾進行監(jiān)控。接下來,您可以在監(jiān)控項目中設置標記并為其創(chuàng)建上下文菜單(contextual menus)。使用來自該應用擴展的API,您也能在Finder窗體中增加工具欄按鈕。

Finder Sync支持可同步本地文件夾與遠程數(shù)據(jù)源內(nèi)容的應用程序。它直接在Finder中提供即時的視覺反饋以改善用戶體驗。標記顯示了每個項目的同步狀態(tài),上下文菜單讓用戶可以管理文件夾內(nèi)容,自定義工具欄按鈕能調(diào)用全局操作,比如打開一個受監(jiān)視的文件夾或者進行同步操作。

提示:Finder Sync擴展僅能讓您修改用戶界面。它不進行實際的同步操作。您需要負責創(chuàng)建自己的同步組件。

Finder Sync擴展有如下功能:

(1).注冊一組用于監(jiān)視的文件夾。

(2).當用戶開始或停止瀏覽受監(jiān)視文件夾的內(nèi)容時收到通知。

比方說,當用戶在Finder或在打開/保存對話框中打開了受監(jiān)視的文件夾時,擴展將會收到消息通知。

(3).在受監(jiān)視文件夾中添加、移除以及更新項目上的標記。

(4).當用戶在受監(jiān)視文件夾中右鍵單擊了一個項目時,顯示一個上下文菜單。

(5).在Finder工具欄上添加自定義按鈕。

和標記以及上下文菜單不同,即使用戶當前沒有瀏覽受監(jiān)視的文件夾,這種按鈕仍然是始終可視的,

2. 指定受監(jiān)視的文件夾

Finder Sync應用擴展的init方法中指定想要監(jiān)視的文件夾,默認使用FIFinderSyncController對象。在絕大多數(shù)情況下,讓用戶在關聯(lián)應用程序提供的用戶界面中指定受監(jiān)視的文件夾。您可以在關聯(lián)應用程序和共享了用戶默認設置的Finder Sync應用擴展之間使用這個數(shù)據(jù)。

激活共享用戶設置,首先在Finder Sync應用擴展和其關聯(lián)應用程序中都添加一個應用程序組(Group)。這個組創(chuàng)建了應用擴展和其關聯(lián)應用程序都可以訪問、共享的容器。打開Xcode中每一個對象的Capabilities窗格,并激活App Groups。 然后對共享組提供唯一標識符。請務必對Finder Sync擴展和其關聯(lián)應用程序使用同一個標識符。

image.png
image.png

接下來,通過調(diào)用initWithSuiteName:方法以及使用共享組中的標識符來實例化一個新的NSUserDefaults對象。這種init方法將創(chuàng)建一個默認的用戶對象,用來加載和保存數(shù)據(jù)到共享容器中。

3. 代碼比較直觀

//
//  FinderSync.m
//  ZZXFinderSync
//
//  Created by  on 2021/11/11.
//

#import "FinderSync.h"

@interface FinderSync ()

@property NSURL *myFolderURL;

@end

@implementation FinderSync

- (instancetype)init {
    self = [super init];

    NSLog(@"%s launched from %@ ; compiled at %s", __PRETTY_FUNCTION__, [[NSBundle mainBundle] bundlePath], __TIME__);

    // Set up the directory we are syncing.
    self.myFolderURL = [NSURL fileURLWithPath:@"/Users/Shared/MySyncExtension Documents"];
    [FIFinderSyncController defaultController].directoryURLs = [NSSet setWithObject:self.myFolderURL];

    // Set up images for our badge identifiers. For demonstration purposes, this uses off-the-shelf images.
    [[FIFinderSyncController defaultController] setBadgeImage:[NSImage imageNamed: NSImageNameColorPanel] label:@"Status One" forBadgeIdentifier:@"One"];
    [[FIFinderSyncController defaultController] setBadgeImage:[NSImage imageNamed: NSImageNameCaution] label:@"Status Two" forBadgeIdentifier:@"Two"];
    [[FIFinderSyncController defaultController] setBadgeImage:[NSImage imageNamed: NSImageNameCaution] label:@"Status Two" forBadgeIdentifier:@"Three"];
    
    [self writeToFile];
    return self;
}

#pragma mark - 徽章處理 文件同步的代理

- (void)beginObservingDirectoryAtURL:(NSURL *)url {
    NSLog(@"beginObservingDirectoryAtURL:%@", url.filePathURL);
    /*
     當用戶開始查看受監(jiān)視文件夾或其子文件夾的內(nèi)容時,系統(tǒng)將會調(diào)用此方法。它將當前打開文件夾的URL作為參數(shù)。
     對于每個唯一的URL,系統(tǒng)只調(diào)用此方法一次。
     只要這些內(nèi)容仍然在至少一個Finder窗口中可見,任何額外打開相同URL的Finder窗口的操作將被忽略。
     系統(tǒng)為所有打開和保存對話框額外創(chuàng)建您擴展的實例。
     這些擴展將自己調(diào)用beginObservingDirectoryAtURL:方法,即使該文件夾已經(jīng)在Finder窗口中打開。
     */
}

- (void)endObservingDirectoryAtURL:(NSURL *)url {
    NSLog(@"endObservingDirectoryAtURL:%@", url.filePathURL);
    /*
     當用戶不再查看給定URL中的內(nèi)容時,系統(tǒng)將會調(diào)用此方法.
     與beginObservingDirectoryAtURL方法一樣,打開和保存對話框分別在Finder中被額外創(chuàng)建擴展實例。
     */
}

- (void)requestBadgeIdentifierForURL:(NSURL *)url {
    NSLog(@"requestBadgeIdentifierForURL:%@", url.filePathURL);
    /*
     當受監(jiān)視文件夾中的一個新項目變得對用戶可見時,系統(tǒng)將會調(diào)用此方法。當每個文件首次在Finder視圖中顯示,這個方法就會被調(diào)用一次。每當新文件滾動到視圖中,系統(tǒng)也會繼續(xù)調(diào)用此方法。

     您通常執(zhí)行此方法來檢查所提供URL對應項目的狀態(tài),然后調(diào)用Finder Sync控制器的setBadgeIdentifier:forURL:方法來設置相應的標記。您可能還想要跟蹤這些URL,只要它們的狀態(tài)發(fā)生變化就更新他們的標記。
     */
    NSString *nsFilePath = [url.filePathURL path];
    NSFileManager *fileManager = [NSFileManager defaultManager];
    
    // 文件夾不處理。
    BOOL isDir;
    [fileManager fileExistsAtPath:nsFilePath isDirectory:&isDir];
    if (isDir) return;
    
    // 獲取文件的修改時間。
    NSDictionary *attrDict = [fileManager attributesOfItemAtPath:nsFilePath error:nil];
    NSDate *modDate = [attrDict objectForKey:NSFileModificationDate];
    NSLog(@"??????   修改時間 ----%@",modDate);
    // /private/var/folders/kt/k000ltc54fz78ffbxjdvg63w0000gn/T/com.zzx.MacWithXPC.ZZXFinderSync
    
    //徽章處理
    NSInteger whichBadge = [url.filePathURL hash] % 3;
    NSString* badgeIdentifier = @[@"One", @"Two", @"three"][whichBadge];
    [[FIFinderSyncController defaultController] setBadgeIdentifier:badgeIdentifier forURL:url];
}

#pragma mark - 菜單處理 添加上下文菜單欄
/// 新增選項
- (NSString *)toolbarItemName {
    return @"ZZXFinderSync";
}
/// 新增選項 -簡介
- (NSString *)toolbarItemToolTip {
    return @"ZZXFinderSync: Click the toolbar item for a menu.";
}
/// 新增選項 - icon
- (NSImage *)toolbarItemImage {
    return [NSImage imageNamed:NSImageNameCaution];
}

- (NSMenu *)menuForMenuKind:(FIMenuKind)whichMenu {
    /*
     打開文件夾時刷新
     選中文件時也會刷新
     */
    
    /*
     下面這個不知道怎么用?
     實現(xiàn)menuForMenuKind:方法以提供一個自定義的上下文菜單。
     該menu參數(shù)指明了您的應用擴展應該創(chuàng)建的菜單類型。每個菜單類型對應不同的用戶交互類型。

     FIMenuKindContextualMenuForItems
     用戶右鍵單擊單個或多個受監(jiān)控文件夾內(nèi)的項目。您的應用擴展應當顯示影響所選項目的菜單項。

     FIMenuKindContextualMenuForContainer
     當用戶瀏覽受監(jiān)控文件夾時右鍵單擊Finder窗口的空白處。您的擴展應當顯示影響當前文件夾內(nèi)容的菜單項。

     FIMenuKindContextualMenuForSidebar
     用戶右鍵單擊顯示受監(jiān)控文件夾及其部分內(nèi)容的側邊欄。您的擴展應當顯示影響所選項內(nèi)容的菜單項。

     FIMenuKindToolbarItemMenu
     用戶單擊擴展提供的導航欄按鈕。由于導航欄按鈕始終可見,用戶此時有可能沒在瀏覽受監(jiān)控的文件夾。您的擴展或許要顯示能始終讓用戶可見的全局操作的菜單欄。如果存在某個菜單項,其仍然可以影響在受監(jiān)控文件夾中所選的項目。
     */

    NSMenu *mainMenu = [[NSMenu alloc] init];
    NSMenuItem *menuItem1 = [[NSMenuItem alloc] initWithTitle:@"菜單1" action:nil keyEquivalent:@""];
    NSMenuItem *menuItem2 = [[NSMenuItem alloc] initWithTitle:@"菜單2" action:nil keyEquivalent:@""];
    [mainMenu addItem:menuItem1];
    [mainMenu insertItem:menuItem2 atIndex:0];
    
    
    //當前選中的文件
    NSURL* target = [[FIFinderSyncController defaultController] targetedURL];
    NSArray* items = [[FIFinderSyncController defaultController] selectedItemURLs];
    if (items.count > 0) {
        //單獨設置菜單的顯示
        NSString *nsFilePath = [items.firstObject path];
        NSLog(@"??????   nsFilePath ----%@----%@",nsFilePath,target.path);
    }
    
    
    NSInteger indexItem = 0;

    NSMenu *subMenu1 = [[NSMenu alloc] init];
    //-*開始 測試用
    NSMenuItem *restoreItem = [[NSMenuItem alloc] initWithTitle:@"選項1" action:@selector(restoreFile:) keyEquivalent:@""];
    [restoreItem setTarget:self];
    [restoreItem setEnabled:YES];
    [subMenu1 insertItem:restoreItem atIndex:indexItem];
    indexItem++;

    NSMenuItem *decryptItem = [[NSMenuItem alloc] initWithTitle:@"選項2" action:@selector(decryptFile:) keyEquivalent:@""];
    [decryptItem setTarget:self];
    [decryptItem setEnabled:YES];
    [subMenu1 insertItem:decryptItem atIndex:indexItem];
    indexItem++;

    NSMenuItem *encryptItem = [[NSMenuItem alloc] initWithTitle:@"選項3" action:@selector(encryptFile:) keyEquivalent:@""];
    [encryptItem setTarget:self];
    [encryptItem setEnabled:YES];
    [subMenu1 insertItem:encryptItem atIndex:indexItem];
    indexItem++;

    NSMenu *subMenu2 = [[NSMenu alloc] init];
    
    NSMenuItem *encryptItem1 = [[NSMenuItem alloc] initWithTitle:@"選項4" action:@selector(encryptFile:) keyEquivalent:@""];
    [encryptItem1 setTarget:self];
    [encryptItem1 setEnabled:YES];
    [subMenu2 insertItem:encryptItem1 atIndex:0];
    indexItem++;
    
    NSMenuItem *encryptItem2 = [[NSMenuItem alloc] initWithTitle:@"選項5" action:@selector(encryptFile:) keyEquivalent:@""];
    [encryptItem2 setTarget:self];
    [encryptItem2 setEnabled:YES];
    [subMenu2 insertItem:encryptItem2 atIndex:1];
    indexItem++;
    
    
    [mainMenu setSubmenu:subMenu1 forItem:menuItem1];
    [mainMenu setSubmenu:subMenu2 forItem:menuItem2];
    return mainMenu;
}

- (IBAction)sampleAction:(id)sender {
    NSURL* target = [[FIFinderSyncController defaultController] targetedURL];
    NSArray* items = [[FIFinderSyncController defaultController] selectedItemURLs];

    NSLog(@"sampleAction: menu item: %@, target = %@, items = ", [sender title], [target filePathURL]);
    [items enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) {
        NSLog(@"文件路徑:%@", [obj path]);
        
        //設置徽標
        NSString *filePath = [obj path];
        [[FIFinderSyncController defaultController] setBadgeIdentifier:@"Three" forURL:[NSURL fileURLWithPath:filePath]];
        
    }];
}

- (IBAction)restoreFile:(id)sender {
    NSURL* target = [[FIFinderSyncController defaultController] targetedURL];
    NSArray* items = [[FIFinderSyncController defaultController] selectedItemURLs];

    NSLog(@"sampleAction: menu item: %@, target = %@, items = ", [sender title], [target filePathURL]);
    [items enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) {
        NSLog(@"    %@", [obj filePathURL]);
    }];
}

- (IBAction)decryptFile:(id)sender {
    NSURL* target = [[FIFinderSyncController defaultController] targetedURL];
    NSArray* items = [[FIFinderSyncController defaultController] selectedItemURLs];

    NSLog(@"sampleAction: menu item: %@, target = %@, items = ", [sender title], [target filePathURL]);
    [items enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) {
        NSLog(@"    %@", [obj filePathURL]);
    }];
}
- (IBAction)encryptFile:(id)sender {
    NSURL* target = [[FIFinderSyncController defaultController] targetedURL];
    NSArray* items = [[FIFinderSyncController defaultController] selectedItemURLs];

    NSLog(@"sampleAction: menu item: %@, target = %@, items = ", [sender title], [target filePathURL]);
    [items enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) {
        NSLog(@"    %@", [obj filePathURL]);
    }];
}

-(void)writeToFile{
    NSString *tmpPath = NSTemporaryDirectory();
    NSString *fileName = [NSString stringWithFormat:@"ZZXFinderSync.log"];// 注意不是NSData!
    NSString *logFilePath = [tmpPath stringByAppendingPathComponent:fileName];
    
//    NSString *logFilePath = @"/Users/mac/Desktop/ZZX/ZZXFinderSync.log";
    // 將log輸入到文件
    freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stdout);
    freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stderr);
}
@end

4. 運行

選擇ZZXFinderSync來運行,依附于Finder,然后Run:

image.png
image.png

運行成功后,打開一個文件夾,就可以看到效果,點擊菜單打印結果。

image.png

或者


image.png

4. 調(diào)試

可以將控制臺打印寫到文件中方便調(diào)試

-(void)writeToFile{
    NSString *tmpPath = NSTemporaryDirectory();
    NSString *fileName = [NSString stringWithFormat:@"ZZXFinderSync.log"];// 注意不是NSData!
    NSString *logFilePath = [tmpPath stringByAppendingPathComponent:fileName];

    // 將log輸入到文件
    freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stdout);
    freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stderr);
}
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容