Sketch 插件開發實踐

本文做者是來自凹凸實驗室 高露 , 他在作內部設計中臺 quark 項目中,協做開發了Quark for Sketch 插件,在開發過程有許多經驗總結,在本文裏與你們分享。javascript

Sketch 是很是流行的 UI 設計工具,2014年隨着 Sketch V43 版本增長 Symbols 功能、開放開發者權限,吸引了大批開發者的關注。html

目前 Sketch 開發有兩大熱門課題:① React 組件渲染成 sketch 由 airbnb 團隊發起,② 使用 skpm 構建開發 Sketch 插件。java

Sketch 插件開發相關資料較少且不太完善,咱們開發插件過程當中能夠重點參考官方文檔,只是有些陳舊。官方有提供 JavaScript API 藉助 CocoaScript bridge 訪問內部 Sketch API 和 macOS 框架進行開發插件(Sketch 53~56 版 JS API 在 native MacOS 和 Sketch API 暴露的特殊環境中運行),提供的底層 API 功能有些薄弱,更深刻的就須要瞭解掌握 Objective-CCocoaScriptAppKitSketch-Headersreact

Sketch 插件結構

Sketch Plugin 是一個或多個 scripts 的集合,每一個 script 定義一個或多個 commands。Sketch Plugin 是以 .sketchplugin 擴展名的文件夾,包含文件和子文件夾。嚴格來講,Plugin 其實是 OS X package,用做爲 OS X bundleios

Bundle 具備標準化分層結構的目錄,其保存可執行代碼和該代碼使用的資源。git

Plugin Bundle 文件夾結構

Bundles 包含一個 manifest.json 文件,一個或多個 scripts 文件(包含用 CocoaScript 或 JavaScript 編寫的腳本),它實現了 Plugins 菜單中顯示的命令,以及任意數量的共享庫腳本和資源文件。github

mrwalker.sketchplugin
  Contents/
    Sketch/
      manifest.json
      shared.js
      Select Circles.cocoascript
      Select Rectangles.cocoascript
    Resources/
      Screenshot.png
      Icon.png
複製代碼

最關鍵的文件是 manifest.json 文件,提供有關插件的信息。web

小貼士:json

Sketch 插件包可使用 skpm 在構建過程當中生成,skpm 提供 Sketch 官方插件模版:canvas

💁 Tip: Any Github repo with a 'template' folder can be used as a custom template:

skpm create <project-name> --template=<username>/<repository>

Manifest

manifest.json 文件提供有關插件的信息,例如做者,描述,圖標、從何處獲取最新更新、定義的命令 **(commands) **、調用菜單項 (menu) 以及資源的元數據。

{
  "name": "Select Shapes",
  "description": "Plugins to select and deselect shapes",
  "author": "Joe Bloggs",
  "homepage": "https://github.com/example/sketchplugins",
  "version": "1.0",
  "identifier": "com.example.sketch.shape-plugins",
  "appcast": "https://excellent.sketchplugin.com/excellent-plugin-appcast.xml",
  "compatibleVersion": "3",
  "bundleVersion": 1,
  "commands": [
    {
      "name": "All",
      "identifier": "all",
      "shortcut": "ctrl shift a",
      "script": "shared.js",
      "handler": "selectAll"
    },
    {
      "name": "Circles",
      "identifier": "circles",
      "script": "Select Circles.cocoascript"
    },
    {
      "name": "Rectangles",
      "identifier": "rectangles",
      "script": "Select Rectangles.cocoascript"
    }
  ],
  "menu": {
    "items": ["all", "circles", "rectangles"]
  }
}
複製代碼

Commands

聲明一組 command 的信息,每一個 command 以 Dictionary 數據結構形式存在。

  • script : 實現命令功能的函數所在的腳本
  • handler : 函數名,該函數實現命令的功能。Sketch 在調用該函數時,會傳入 context 上下文參數。若未指定 handler,Sketch 會默認調用對應 script 中 onRun 函數
  • shortcut:命令的快捷鍵
  • name:顯示在 Sketch Plugin 菜單中
  • identifier : 惟一標識,建議用 com.xxxx.xxx 格式,不要過長

Menu

Sketch 加載插件會根據指定的信息,在菜單欄中有序顯示命令名。

在瞭解了 Sketch 插件結構以後,咱們再來了解一下,sketch提供的官方 API: Actions API, Javascript API。

Sketch Actions API

Sketch Actions API 用於監聽用戶操做行爲而觸發事件,例如 OpenDocumen(打開文檔)、CloseDocument(關閉文檔)、Shutdown(關閉插件)、TextChanged(文本變化)等,具體詳見官網:developer.sketch.com/reference/a…

  • register Actions

manifest.json 文件,配置相應 handlers。

示例:當 OpenDocument 事件被觸發時調用 onOpenDocument handler 。

"commands" : [
  ...
  {
    "script" : "my-action-listener.js",
    "name" : "My Action Listener",
    "handlers" : {
      "actions": {
        "OpenDocument": "onOpenDocument"
      }
    },
    "identifier" : "my-action-listener-identifier"
  }
  ...
],
複製代碼

**my-action-listener.js **

export function onOpenDocument(context) {  	  		
  context.actionContext.document.showMessage('Document Opened')
}
複製代碼
  • Action Context

Action 事件觸發時會將 context.actionContext 傳遞給相應 handler。注意有些 Action 包含兩個狀態beginfinish,例如 SelectionChanged,需分別訂閱 SelectionChanged.beginSelectionChanged.finish,不然會觸發兩次事件。

Sketch JS API

Sketch 插件開發大概有以下三種方式:① 純使用 CocoaScript 腳本進行開發,② 經過 Javascript + CocoaScript 的混合開發模式, ③ 經過 AppKit + Objective-C 進行開發。Sketch 官方建議使用 JavaScript API 編寫 Sketch 插件,且官方針對 Sketch Native API 封裝了一套 JS API,目前還未涵蓋全部場景, 若須要更豐富的底層 API 需結合 CocoaScript 進行實現。經過 JS API 能夠很方便的對 Sketch 中 DocumentArtboardGroupLayer 進行相關操做以及導入導出等,可能須要考慮兼容性, JS API 原理圖以下:

api-reference

CocoaScript

CocoaScript 實現 JavaScript 運行環境到 Objective-C 運行時的橋接功能,可經過橋接器編寫 JavaScript 外部腳本訪問內部 Sketch API 和 macOS 框架底層豐富的 API 功能。

小貼士:

Mocha 實現提供 JavaScript 運行環境到 Objective-C 運行時的橋接功能已包含在CocoaScript中。

CocoaScript 創建在 Apple 的 JavaScriptCore 之上,而 JavaScriptCore 是爲 Safari 提供支持的 JavaScript 引擎,使用 CocoaScript 編寫代碼實際上就是在編寫 JavaScript。CocoaScript 包括橋接器,能夠從 JavaScript 訪問 Apple 的 Cocoa 框架。

藉助 CocoaScript 使用 JavaScript 調 Objective-C 語法:

  • 方法調用用 ‘.’ 語法
  • Objective-C 屬性設置
    • Getter: object.name()
    • Setter: object.setName('Sketch')object.name='sketch'
  • 參數都放在 ‘ ( ) ’ 裏
  • Objective-C 中 ' : '(參數與函數名分割符) 轉換爲 ' _ ',最後一個下劃線是可選的
  • 返回值,JavaScript 統一用 var/const/let 設置類型

注意:詳細 Objective-C to JavaScript 請參考 Mocha 文檔

示例:

// oc: MSPlugin 的接口 valueForKey:onLayer:
NSString * value = [command valueForKey:kAutoresizingMask onLayer:currentLayer];

// cocoascript:
const value = command.valueForKey_onLayer(kAutoresizingMask, currentLayer);

// oc:
const app = [NSApplication sharedApplication];
[app displayDialog:msg withTitle:title];

// cocoascript:
const app = NSApplication.sharedApplication();
app.displayDialog_withTitle(msg, title)

// oc:
const openPanel = [NSOpenPanel openPanel]
[openPanel setTitle: "Choose a location…"]
[openPanel setPrompt: "Export"];

// cocoascript:
const openPanel = NSOpenPanel.openPanel
openPanel.setTitle("Choose a location…")
openPanel.setPrompt("Export")

複製代碼

Objective-C Classes

Sketch 插件系統能夠徹底訪問應用程序的內部結構和 macOS 中的核心框架。Sketch 是用 Objective-C 構建的,其 Objective-C 類經過 Bridge (CocoaScript/mocha) 提供 Javascript API 調用,簡單的瞭解 Sketch 暴露的相關類以及類方法,對咱們開發插件很是有幫助。

使用 Bridge 定義的一些內省方法來訪問如下信息:

String(context.document.class()) // MSDocument

const mocha = context.document.class().mocha()

mocha.properties() // array of MSDocument specific properties defined on a MSDocument instance
mocha.propertiesWithAncestors() // array of all the properties defined on a MSDocument instance

mocha.instanceMethods() // array of methods defined on a MSDocument instance
mocha.instanceMethodsWithAncestors()

mocha.classMethods() // array of methods defined on the MSDocument class
mocha.classMethodsWithAncestors()

mocha.protocols() // array of protocols the MSDocument class inherits from
mocha.protocolsWithAncestors()
複製代碼

Context

當輸入插件定製的命令時,Sketch 會去尋找改命令對應的實現函數, 並傳入 context 變量。context包含如下變量:

  • command: MSPluginCommand 對象,當前執行命令
  • document: MSDocument 對象 ,當前文檔
  • plugin: MSPluginBundle 對象,當前的插件 bundle,包含當前運行的腳本
  • scriptPath: NSString 當前執行腳本的絕對路徑
  • scriptURL: 當前執行腳本的絕對路徑,跟 **scriptPath **不一樣的是它是個 NSURL 對象
  • selection: 一個 NSArray 對象,包含了當前選擇的全部圖層。數組中的每個元素都是 MSLayer 對象

小貼士:MS 打頭類名爲 Sketch 封裝類如圖層基類 MSLayer、文本層基類 MSTextLayer 、位圖層基類 MSBitmapLayer,NS 打頭爲 AppKit 中含有的類

const app = NSApplication.sharedApplication()

function initContext(context) {
		context.document.showMessage('初始執行腳本')
		
    const doc = context.document
    const page = doc.currentPage()
    const artboards = page.artboards()
    const selectedArtboard = page.currentArtboard() // 當前被選擇的畫板
    
    const plugin = context.plugin
    const command = context.command
    const scriptPath = context.scriptPath
    const scriptURL = context.scriptURL
    const selection = context.selection // 被選擇的圖層
}
複製代碼

##Sketch 插件開發上手

前面咱們瞭解了許多 Sketch 插件開發知識,那接下來實際上手兩個小例子: ① 建立輔助內容面板窗口, ② 側邊欄導航。爲了方便開發,咱們在開發前需先進行以下操做:

崩潰保護

當 Sketch 運行發生崩潰,它會停用全部插件以免循環崩潰。對於使用者,每次崩潰重啓後手動在菜單欄啓用所需插件很是繁瑣。所以能夠經過以下命令禁用該特性。

defaults write com.bohemiancoding.sketch3 disableAutomaticSafeMode true
複製代碼

插件緩存

經過配置啓用或禁用緩存機制:

defaults write com.bohemiancoding.sketch3 AlwaysReloadScript -bool YES
複製代碼

該方法對於某些場景並不適用,如設置 COScript.currentCOScript().setShouldKeepAround(true) 區塊會保持常駐在內存,那麼則須要經過 coscript.setShouldKeepAround(false) 進行釋放。

WebView 調試

若是插件實現方案使用 WebView 作界面,可經過如下配置開啓調試功能。

defaults write com.bohemiancoding.sketch3 WebKitDeveloperExtras -bool YES
複製代碼

建立輔助內容面板窗口

首先咱們先熟悉一下 macOS 下的輔助內容面板, 以下圖最左側 NSPanel 樣例, 它是有展現區域,可設置樣式效果,左上角有可操做按鈕的輔助窗口。

Sketch 中要建立以下內容面板,須要使用 macOS 下 AppKit 框架中 NSPanel 類,它是 NSWindow 的子類,用於建立輔助窗口。內容面板外觀樣式設置,可經過 NSPanel 類相關屬性進行設置, 也可經過 AppKitNSVisualEffectView 類添加模糊的背景效果。內容區域則可經過 AppKitWKWebView 類,單開 webview 渲染網頁內容展現。

console

  • 建立 Panel
const panelWidth = 80;
const panelHeight = 240;

// Create the panel and set its appearance
const panel = NSPanel.alloc().init();
panel.setFrame_display(NSMakeRect(0, 0, panelWidth, panelHeight), true);
panel.setStyleMask(NSTexturedBackgroundWindowMask | NSTitledWindowMask | NSClosableWindowMask | NSFullSizeContentViewWindowMask);
panel.setBackgroundColor(NSColor.whiteColor());

// Set the panel's title and title bar appearance
panel.title = "";
panel.titlebarAppearsTransparent = true;

// Center and focus the panel
panel.center();
panel.makeKeyAndOrderFront(null);
panel.setLevel(NSFloatingWindowLevel);

// Make the plugin's code stick around (since it's a floating panel)
COScript.currentCOScript().setShouldKeepAround(true);

// Hide the Minimize and Zoom button
panel.standardWindowButton(NSWindowMiniaturizeButton).setHidden(true);
panel.standardWindowButton(NSWindowZoomButton).setHidden(true);
複製代碼
  • Panel 添加模糊的背景
// Create the blurred background
const vibrancy = NSVisualEffectView.alloc().initWithFrame(NSMakeRect(0, 0, panelWidth, panelHeight));
vibrancy.setAppearance(NSAppearance.appearanceNamed(NSAppearanceNameVibrantLight));
vibrancy.setBlendingMode(NSVisualEffectBlendingModeBehindWindow);

// Add it to the panel
panel.contentView().addSubview(vibrancy);
複製代碼
  • Panel 插入 webview 渲染
const wkwebviewConfig = WKWebViewConfiguration.alloc().init()
  const webView = WKWebView.alloc().initWithFrame_configuration(
    CGRectMake(0, 0, panelWidth, panelWidth),
    wkwebviewConfig
  )
  
  // Add it to the panel
  panel.contentView().addSubview(webView);
  
  // load file URL
  webview.loadFileURL_allowingReadAccessToURL(
    NSURL.URLWithString(url),
    NSURL.URLWithString('file:///')
  )
複製代碼

側邊欄導航開發

咱們開發複雜的 Sketch 插件,通常都要開發側邊欄導航展現插件功能按鈕,點擊觸發相關操做。那開發側邊欄導航,咱們主要使用 AppKit 中的那些類呢,有 NSStackViewNSBoxNSImageNSImageViewNSButton 等,大體核心代碼以下:

// create toolbar
  const toolbar = NSStackView.alloc().initWithFrame(NSMakeRect(0, 0, 40, 400))
  threadDictionary[SidePanelIdentifier] = toolbar
  toolbar.identifier = SidePanelIdentifier
  toolbar.setSpacing(8)
  toolbar.setFlipped(true)
  toolbar.setBackgroundColor(NSColor.windowBackgroundColor())
  toolbar.orientation = 1
	
  // add element
  toolbar.addView_inGravity(createImageView(NSMakeRect(0, 0, 40, 22), 'transparent', NSMakeSize(40, 22)), 1)
  const Logo = createImageView(NSMakeRect(0, 0, 40, 30), 'logo', NSMakeSize(40, 28))
  toolbar.addSubview(Logo)

  const contentView = context.document.documentWindow().contentView()
  const stageView = contentView.subviews().objectAtIndex(0)

  const views = stageView.subviews()
  const existId = views.find(d => ''.concat(d.identifier()) === identifier)

  const finalViews = []

  for (let i = 0; i < views.count(); i++) {
    const view = views[i]
    if (existId) {
      if (''.concat(view.identifier()) !== identifier) finalViews.push(view)
    } else {
      finalViews.push(view)
      if (''.concat(view.identifier()) === 'view_canvas') {
        finalViews.push(toolbar)
      }
    }
  }

	// add to main Window
  stageView.subviews = finalViews
  stageView.adjustSubviews()
複製代碼

詳細見開源代碼: github.com/o2team/sket… (歡迎 star 交流)

調試

當插件運行時,Sketch 將會建立一個與其關聯的 JavaScript 上下文,可使用 Safari 來調試該上下文。

在 Safari 中, 打開 Developer > 你的機器名稱 > Automatically Show Web Inspector for JSContexts,同時啓用選項 Automatically Pause Connecting to JSContext,不然檢查器將在能夠交互以前關閉(當腳本運行完時上下文會被銷燬)。

如今就能夠在代碼中使用斷點了,也能夠在運行時檢查變量的值等等。

日誌

JavaScriptCore 運行 Sketch 插件的環境 也有提供相似調試 JavaScript 代碼打 log 的方式,咱們能夠在關鍵步驟處放入一堆 console.log/console.error 等進行落點日誌查看。

有如下幾種選擇能夠查看日誌:

  • 打開 Console.app 並查找 Sketch 日誌
  • 查看 ~/Library/Logs/com.bohemiancoding.sketch3/Plugin Output.log 文件
  • 運行 skpm log 命令,該命令能夠輸出上面的文件(執行 skpm log -f 能夠流式地輸出日誌)
  • 使用 skpm 開發的插件,安裝 sketch-dev-tools,使用 console.log 打日誌查看。

console

SketchTool

SketchTool 包含在 Sketch 中的 CLI 工具,經過 SketchTool 可對 Sketch 文檔執行相關操做:

sketchtool 二進制文件位於 Sketch 應用程序包中:

Sketch.app/Contents/Resources/sketchtool/bin/sketchtool
複製代碼

設置 alias

alias sketchtool="/Applications/Sketch.app/Contents/Resources/sketchtool/bin/sketchtool"
複製代碼

使用:

sketchtool -h  # 查看幫助
sketchtool export artboards path/to/document.sketch  # 導出畫板
sketchtool dump path/to/document.sketch # 導出 Sketch 文檔 JSON data
sketchtool metadata path/to/document.sketch # 查看 Sketch 文檔元數據
sketchtool run [Plugin path] # 運行插件
複製代碼

注意:SketchTool 須要 OSX 10.11或更高版本。

Other Resources

sketch Plugin 開發官方文檔

sketch插件開發中文文檔

sketch 使用文檔

sketch-utils

sketch reference api

Github SketchAPI

react-sketchapp

Sketch-Plugins-Cookbook

iOS開發60分鐘入門

AppKit, 構建 Sketch 的一個主要 Apple 框架

Foundation(基礎), 更重要的 Apple 課程和服務

Chromeless-window

歡迎關注凹凸實驗室博客:aotu.io

或者關注凹凸實驗室公衆號(AOTULabs),不定時推送文章:

相關文章
相關標籤/搜索