Macos开发:Finder Sync扩展的基本使用

FinderSync扩展允许开发者为MacOS的Finder添加自定义右键菜单项和工具栏功能,如新建文件、解压等。通过Xcode创建FinderSync扩展项目,设置监控路径并编写相应方法来响应用户操作。文章介绍了如何进行工程配置、调试运行以及日志输出,以帮助开发者解决扩展中的问题。徽章功能因启动顺序限制可能无法稳定显示。
摘要由CSDN通过智能技术生成

Finder Sync扩展介绍

当你用习惯了Windows,刚换到Mac会有各种不适应,比如右键菜单里不能新建txt文件,用惯了图形界面操作,svn、git都要使用命令行来操作,打开终端进入当前目录也要命令行,对于很多非开发人员来说这些操作很不友好,即使是开发人员我也想减少操作步骤,节省时间。

以上这些问题通过Finder Sync扩展都可以解决,你可以通过它来为你的右键菜单增加"新建txt"、“解压rar”、"当前目录打开终端"等功能项,你也可以把命令行使用的svn、git包装成图形界面工具,使用右键菜单和工具栏来灵活使用它。

总结Finder Sync扩展的使用场景:可以为你改造finder的右键菜单和工具栏菜单,至于点击菜单可以实现的功能你可以自由发挥,如果要上架到Appstore,功能设计上要考虑沙盒的局限性和权限的适用范围。

Xcode新建工程

在这里插入图片描述在这里插入图片描述

新建Finder Sync扩展Target

在这里插入图片描述在这里插入图片描述在这里插入图片描述
Target会自动生成如下代码

import Cocoa
import FinderSync

class FinderSync: FIFinderSync {

    var myFolderURL = URL(fileURLWithPath: "/Users/Shared/MySyncExtension Documents")
    
    override init() {
        super.init()
        
        NSLog("FinderSync() launched from %@", Bundle.main.bundlePath as NSString)
        
        // Set up the directory we are syncing.
        FIFinderSyncController.default().directoryURLs = [self.myFolderURL]
        
        // Set up images for our badge identifiers. For demonstration purposes, this uses off-the-shelf images.
        FIFinderSyncController.default().setBadgeImage(NSImage(named: NSImage.colorPanelName)!, label: "Status One" , forBadgeIdentifier: "One")
        FIFinderSyncController.default().setBadgeImage(NSImage(named: NSImage.cautionName)!, label: "Status Two", forBadgeIdentifier: "Two")
    }
    
    // MARK: - Primary Finder Sync protocol methods
    
    override func beginObservingDirectory(at url: URL) {
        // The user is now seeing the container's contents.
        // If they see it in more than one view at a time, we're only told once.
        NSLog("beginObservingDirectoryAtURL: %@", url.path as NSString)
    }
    
    
    override func endObservingDirectory(at url: URL) {
        // The user is no longer seeing the container's contents.
        NSLog("endObservingDirectoryAtURL: %@", url.path as NSString)
    }
    
    override func requestBadgeIdentifier(for url: URL) {
        NSLog("requestBadgeIdentifierForURL: %@", url.path as NSString)
        
        // For demonstration purposes, this picks one of our two badges, or no badge at all, based on the filename.
        let whichBadge = abs(url.path.hash) % 3
        let badgeIdentifier = ["", "One", "Two"][whichBadge]
        FIFinderSyncController.default().setBadgeIdentifier(badgeIdentifier, for: url)
    }
    
    // MARK: - Menu and toolbar item support
    
    override var toolbarItemName: String {
        return "FinderSy"
    }
    
    override var toolbarItemToolTip: String {
        return "FinderSy: Click the toolbar item for a menu."
    }
    
    override var toolbarItemImage: NSImage {
        return NSImage(named: NSImage.cautionName)!
    }
    
    override func menu(for menuKind: FIMenuKind) -> NSMenu {
        // Produce a menu for the extension.
        let menu = NSMenu(title: "")
        menu.addItem(withTitle: "Example Menu Item", action: #selector(sampleAction(_:)), keyEquivalent: "")
        return menu
    }
    
    @IBAction func sampleAction(_ sender: AnyObject?) {
        let target = FIFinderSyncController.default().targetedURL()
        let items = FIFinderSyncController.default().selectedItemURLs()
        
        let item = sender as! NSMenuItem
        NSLog("sampleAction: menu item: %@, target = %@, items = ", item.title as NSString, target!.path as NSString)
        for obj in items! {
            NSLog("    %@", obj.path as NSString)
        }
    }

}

这里设置你的FinderSync监控路径,finder处于如下路径及子路径中,你的扩展程序才会生效

var myFolderURL = URL(fileURLWithPath: "/Users/Shared/MySyncExtension Documents")

如果你要监控整个磁盘可以设置为

var myFolderURL = URL(fileURLWithPath: "/")

进入设置的监控路径及子路径触发

override func beginObservingDirectory(at url: URL) {
	// The user is now seeing the container's contents.
    // If they see it in more than one view at a time, we're only told once.
    NSLog("beginObservingDirectoryAtURL: %@", url.path as NSString)
}

离开触发如下方法

override func endObservingDirectory(at url: URL) {
	// The user is no longer seeing the container's contents.
   	NSLog("endObservingDirectoryAtURL: %@", url.path as NSString)
}

工具栏的变化

在这里插入图片描述
工具栏菜单项名称、鼠标悬停提示文字以及图标的设置在如下方法内

override var toolbarItemName: String {
    return "FinderSy"
}

override var toolbarItemToolTip: String {
    return "FinderSy: Click the toolbar item for a menu."
}

override var toolbarItemImage: NSImage {
    return NSImage(named: NSImage.cautionName)!
}

finder中的右键菜单以及工具栏菜单的显示都会在下面方法内处理

override func menu(for menuKind: FIMenuKind) -> NSMenu {
    let menu = NSMenu(title: "")
    //右键点击空白区域显示的菜单
    if(menuKind == FIMenuKind.contextualMenuForItems) {
        menu.addItem(withTitle: "Context Menu Item context", action: #selector(sampleAction(_:)), keyEquivalent: "")
    }
    //右键点击文件或文件夹显示的菜单
    else if(menuKind == FIMenuKind.contextualMenuForItems) {
        menu.addItem(withTitle: "Context Item Menu Item", action: #selector(sampleAction(_:)), keyEquivalent: "")
    }
    //点击工具栏图标显示的菜单
    else if(menuKind == FIMenuKind.toolbarItemMenu) {
        menu.addItem(withTitle: "Toolbar Menu Item", action: #selector(sampleAction(_:)), keyEquivalent: "")
    }
    //右键点击侧栏显示的菜单
    else if(menuKind == FIMenuKind.contextualMenuForSidebar) {
        menu.addItem(withTitle: "Sidebar Menu Item", action: #selector(sampleAction(_:)), keyEquivalent: "")
    }
    return menu
}

调试运行显示右键菜单

确保finder扩展已勾选
在这里插入图片描述
运行和普通应用不同,需要选择一个应用,选择finder即可
在这里插入图片描述
右键点击菜单如图
在这里插入图片描述

日志输出调试

finder扩展程序的调试没办法运行断点调试,也不能控制台输出,调试是个很棘手的问题,添加如下方法把控制台输出写入日志文件,通过查看日志文件来一点点调试开发。如果某个操作后突然发现右键菜单消失了或者工具栏中的菜单项不见了,就去看日志吧,十之八九是异常退出了!

func initLogSetting(){
    let tmpPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as NSURL
    let fileName = "/out.log"// 注意不是NSData!
    let logFilePath = tmpPath.path!.appending(fileName)
    freopen(logFilePath.cString(using: .utf8), "a+", stdout)
    freopen(logFilePath.cString(using: .utf8), "a+", stderr);
    //writeLog(str: logFilePath)
}

修改override init()方法增加

override init() {
    super.init()
    initLogSetting()
    NSLog("FinderSync() launched from %@", Bundle.main.bundlePath as NSString)
    
    // Set up the directory we are syncing.
    FIFinderSyncController.default().directoryURLs = [self.myFolderURL]
    
    // Set up images for our badge identifiers. For demonstration purposes, this uses off-the-shelf images.
    FIFinderSyncController.default().setBadgeImage(NSImage(named: NSImage.colorPanelName)!, label: "Status One" , forBadgeIdentifier: "One")
    FIFinderSyncController.default().setBadgeImage(NSImage(named: NSImage.cautionName)!, label: "Status Two", forBadgeIdentifier: "Two")
}

输出日志目录,因为启用了沙盒,文件会创建到沙盒目录下,

/Users/{用户}/Library/Containers/com.test.Macos-FinderSyncExtension.FinderSyncExt/Data/Documents/out.log

添加徽章

徽章功能不太实用,太多应用为了把自己的功能增加到右键菜单或工具栏中都实现了Finder扩展,例如解压缩软件、有道词典之类,徽章是否能显示成功取决于你的finder启动顺序是否是第一个或干脆是唯一一个,实际中不手动干预扩展设置,基本没机会显示出来。
在这里插入图片描述
指定可使用的徽章图标

// Set up images for our badge identifiers. For demonstration purposes, this uses off-the-shelf images.
FIFinderSyncController.default().setBadgeImage(NSImage(named: NSImage.colorPanelName)!, label: "Status One" , forBadgeIdentifier: "One")
FIFinderSyncController.default().setBadgeImage(NSImage(named: NSImage.cautionName)!, label: "Status Two", forBadgeIdentifier: "Two")

显示徽章,不是唯一的一个finder扩展或者启动顺序排第一个,就不要想进入这个方法了,都不会被调用到,鸡肋的功能

override func requestBadgeIdentifier(for url: URL) {
    NSLog("requestBadgeIdentifierForURL: %@", url.path as NSString)
    
    // For demonstration purposes, this picks one of our two badges, or no badge at all, based on the filename.
    let whichBadge = abs(url.path.hash) % 2
    let badgeIdentifier = ["One", "Two"][whichBadge]
    FIFinderSyncController.default().setBadgeIdentifier(badgeIdentifier, for: url)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ArslanRobot

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值