Sketch插件開發入門

插件結構

http://developer.sketchapp.com/introduction/plugin-bundles/

sketch 插件就是腳本的集合。每個腳本定義了一個或者多個命令。這些命令可以拓展sketch的功能。sketch插件以 .sketchplugin結尾,其實就是個文件夾,把后綴刪除后可以直接打開。

example.sketchplugin
  Contents/
    Sketch/
      manifest.json
      script.cocoascript
    Resources/
      XXX.png
      XXX.png

manifest.json

這個json文件聲明插件的元數據.Sketch會讀取這個配置文件的信息,得到command的名字和command對應函數實現.
這文件里面最重要的配置項是Commands和Menu:

Commands

聲明一組command的信息。每個command以字典形式存在。

  • script : 實現命令功能的函數所在的腳本
  • handler: 函數名,該函數實現命令的功能。Sketch在調用該函數時,會傳入一個context參數,context是個字典,里面包含了很多信息,下文會講到。如果你的沒有指定handler,Sketch會默認是script里的onRun函數
  • shortcut:命令的快捷鍵
  • name:會顯示在Sketch的Plugin 的菜單里
  • identifier:唯一標識,建議用com.xxxx.xxx格式
Menu

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

{
  "author" : "rongyan.zry",
  "commands" : [
    {
      "script" : "script.cocoascript",
      "handler" : "setFixedMasks",
      "shortcut" : "option m",
      "name" : "Artboard Mask",
      "identifier" : "com.rongyanzry.layermask"
    },
    {
      "script" : "script.cocoascript",
      "handler" : "onRun",
      "shortcut" : "option s",
      "name" : "Artboard Scale",
      "identifier" : "com.rongyanzry.artboardscale"
    }
  ],
  "menu" : {
    "items" : [
      "com.rongyanzry.layermask",
      "com.rongyanzry.artboardscale"
    ],
    "title" : "Artboard Scale"
  },
  "identifier" : "com.rongyanzry.sketch.9a776995-f790-4048-870b-881639f52bcc",
  "version" : "1.0",
  "description" : "A plugin that helps you to scale artboards and layers",
  "authorEmail" : ".....@gmail.com",
  "name" : "Artboard Scale"
}

語言CocoaScript

入門

This is a bridge that lets you write JavaScript scripts that can call native Objective-C/Cocoa.Using it, you can write the logic of your Plugin in JavaScript, but can call the actual classes and methods that implement Sketch itself when you want to make it do something.

CocoaScript.

在使用中用js調objective-c的方法時,

  • 方法調用 用 ‘.’ 語法
  • 參數都放在‘()’里頭
  • oc中被參數分割開的函數名,用 ‘ _ ’連接.
  • 返回值,js統一用var類型 ,js是弱類型

比如: MSPlugin的接口valueForKey:onLayer:
oc:

NSString * value = [command valueForKey:kAutoresizingMask onLayer:currentLayer];

cocoascript:

var value = command.valueForKey_onLayer(kAutoresizingMask, currentLayer);

Context

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

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

// context的到的信息

var app = NSApplication.sharedApplication(),
    selection,
    plugin,
    command,
    doc,
    page,
    artboards,
    selectedArtboard;

function initContext(context) {
    doc = context.document,
        plugin = context.plugin,
        command = context.command,
        page = doc.currentPage(),
        artboards = page.artboards(),  
        selectedArtboard = page.currentArtboard(),  // 當前被選擇的畫板
        selection = context.selection,   // 被選擇的圖層
        
  for (var i=0; i<selection.count(); i++) {
    var layer = selection[i]
    log('layer ' + layer.name + ' is selected.')
  }
}
 
command:

MSPluginCommand對象,代表插件中單個命令,可以用來訪問當前執行的命令,也可以用來在圖層中存儲自己的擴展元數據。

valueForKey:onLayer
setValue:forKey:onLayer:

在圖層中存取的拓展數據,也可以在不同插件中共享。

valueForKey:onLayer:forPluginIdentifier:
setValue:forKey:onLayer:forPluginIdentifier:
document
showMessage:(NSString)message // 在sketch底部彈出信息,我經常用來打日志。。。哭

selection

當前選擇的document下的一組被選擇的MSLayers對象。
如果當前只選了一個圖層,可以這樣獲取:

var currentLayer = (selection.count() > 0) ? selection[0] : undefined

編譯環境

運行和debug

主要依賴打log.用Console.app過濾日志查看,過濾標簽可以是你寫的插件的名字,也可以用“sketch”過濾,但是這樣會得到所有命令的執行日志。

log("The value of count is:" + count)

或者在代碼中用視圖控件顯示,好處就是不用不用開第三方app.比如:

alert, [doc showMessage]showMessage("message")

Sketch-Header

有人dump出來的Sketch的頭文件。信息量非常豐富哈哈~
https://github.com/abynim/Sketch-Headers

例子:

獲取畫板的所有layer:

以這個設計圖為例
https://github.com/bouchenoiremarc/Sketch-Constraints/blob/master/Example.sketch

var artboardEnumerator = artboards.objectEnumerator();
while (artboard = artboardEnumerator.nextObject()) {
    //var layers = artboard.layers().array();
    //var layers = artboard.children();
    log(layers);
}
//  layers返回畫布第一層的 subLayer
//var layers = artboard.layers().array();
/**
 *
 "<MSLayerGroup: 0x6180001d3b00> Group (741958C5-E1CE-468F-9AB2-E853C0799423)",
 "<MSShapeGroup: 0x7f82e052c260> Camera (7E5D9F7A-2C5A-4223-AD83-8C5F340AC5EF)",
 "<MSShapeGroup: 0x7f82e052de30> Control Center Grabber (936460F8-5773-4F47-89C2-C4BD246C63EC)",
 "<MSLayerGroup: 0x6180001d3bf0> Slide to unlock (A240F642-0036-4629-A365-D0F537A744F8)",
 "<MSTextLayer: 0x7f82e052df40> Date (9BE84955-A291-431D-8771-C36D51743FDD)",
 "<MSTextLayer: 0x7f82e052e080> Time (FCF1C9FD-D6FD-4FFD-9B5F-B9D2635584BB)",
 "<MSLayerGroup: 0x6180001d3ce0> Charge (6F95F078-56BC-4FDA-A319-DE0FF8C50E13)",
 "<MSShapeGroup: 0x7f82e052e1c0> Notification Center Grabber (75CA34DC-7FA7-4D5B-96AB-922C23F74DC5)",
 "<MSShapeGroup: 0x7f82e052e2d0> Wifi (38D4CE87-0826-405C-B13C-D30A6EE16437)",
 "<MSLayerGroup: 0x6180001d3dd0> Signal (5B08C199-0398-45CC-9905-A8B16EFB5B71)",
 "<MSTextLayer: 0x7f82e052e3e0> @constraints (7E04B443-1D85-48FB-B4C6-2334FE7A6408)"
 **/


//     當前畫板的全部圖層
//     var layers = artboard.children();

/**
 * (
 "<MSBitmapLayer: 0x7f82e0761c20> Wallpaper (83A6D488-6FE4-46A9-8CCF-8DBB12C1F845)",
 "<MSLayerGroup: 0x6180001d3b00> Group (741958C5-E1CE-468F-9AB2-E853C0799423)",
 "<MSRectangleShape: 0x610000193ce0> Path (D428D7A1-042A-4700-9D59-3CC601B79047)",
 "<MSShapePathLayer: 0x610000364c80> Path (A98F99AE-2B01-49F9-B8AB-41B9913581D4)",
 "<MSShapePathLayer: 0x610000364d40> Path (0B844654-8140-4A20-B090-3B41ACBBBF25)",
 "<MSOvalShape: 0x610000364e00> Oval (0001F0C5-032D-47C7-BD66-41262D08D9AB)",
 "<MSRectangleShape: 0x610000193db0> Rectangle (7930B492-435C-41EF-B802-7B36297D4A67)",
 "<MSShapeGroup: 0x7f82e052c260> Camera (7E5D9F7A-2C5A-4223-AD83-8C5F340AC5EF)",
 "<MSRectangleShape: 0x610000193c10> Path (C66C2797-E56B-4D69-9D25-B68614EEE93C)",
 "<MSShapeGroup: 0x7f82e052de30> Control Center Grabber (936460F8-5773-4F47-89C2-C4BD246C63EC)",
 "<MSTextLayer: 0x7f82e0761a20> Text (8D49531C-EE6F-44DB-BD5B-6CB1FC6BC8E5)",
 "<MSShapePathLayer: 0x600000361800> Path (7B83C7E2-AA83-4DA4-81A5-F20AE9499B99)",
 "<MSShapeGroup: 0x7f82e07613d0> Arrow (8A5109A2-945D-4D12-BF08-9026FE97D639)",
 "<MSLayerGroup: 0x6180001d3bf0> Slide to unlock (A240F642-0036-4629-A365-D0F537A744F8)",
 "<MSTextLayer: 0x7f82e052df40> Date (9BE84955-A291-431D-8771-C36D51743FDD)",
 "<MSTextLayer: 0x7f82e052e080> Time (FCF1C9FD-D6FD-4FFD-9B5F-B9D2635584BB)",
 "<MSShapePathLayer: 0x61800017c500> Path (D46E9CEA-1918-4070-8247-55D88D2F6A4F)",
 "<MSShapePathLayer: 0x61800017c5c0> Path (23F3EAA0-5970-4746-BB42-3E49B5C346A5)",
 "<MSShapeGroup: 0x7f82e075d200> Battery (F05E72B0-160F-4997-AB7F-B10F7FB6B9B8)",
 "<MSRectangleShape: 0x618000191d30> Path (8A682D4A-34CE-44DA-A986-BFAAFA448E48)",
 "<MSShapeGroup: 0x7f82e075fac0> Fill (3AF6A505-68FE-4BAD-9BA5-4C859D8548F6)",
 "<MSShapePathLayer: 0x61800017c680> Path (3A64AA1C-E554-4B86-A41D-67323D4B04D1)",
 "<MSShapeGroup: 0x7f82e075fbd0> + (BA404D3E-327F-4618-81A2-4371F04482CF)",
 "<MSLayerGroup: 0x6180001d3ce0> Charge (6F95F078-56BC-4FDA-A319-DE0FF8C50E13)",
 "<MSRectangleShape: 0x6100001938d0> Path (BE0B6A98-1C60-4DCB-B464-526E4FAC6950)",
 "<MSShapeGroup: 0x7f82e052e1c0> Notification Center Grabber (75CA34DC-7FA7-4D5B-96AB-922C23F74DC5)",
 "<MSShapePathLayer: 0x610000364500> Path (332FBE18-C807-4C6E-938D-62178B9F620E)",
 "<MSShapePathLayer: 0x610000364200> Path (2E267A2D-E7E0-44DB-91C1-4D79B1FE0FD8)",
 "<MSShapePathLayer: 0x610000364b00> Path (BF7FAE10-2DC1-4ADF-B3D0-A851AAB969C7)",
 "<MSShapeGroup: 0x7f82e052e2d0> Wifi (38D4CE87-0826-405C-B13C-D30A6EE16437)",
 "<MSOvalShape: 0x61800017c740> Path (64D27B50-F968-4E57-B68E-64736A85AC85)",
 "<MSShapeGroup: 0x7f82e075d910> Full (453FF4A7-D7D3-4A11-BE26-77C86E5F4016)",
 "<MSOvalShape: 0x61800017c800> Path (0DD8FB65-2650-44BE-B4C6-E5CB9A76E59E)",
 "<MSShapeGroup: 0x7f82e075da20> Full (9AD329E6-7ED9-48AB-946A-991EEF1E1170)",
 "<MSOvalShape: 0x61800017c8c0> Path (4EA334DD-A423-4C39-B8A2-5F3656A95E73)",
 "<MSShapeGroup: 0x7f82e075ced0> Full (4E2540EB-BCB8-41CB-9338-9146DC678E71)",
 "<MSShapePathLayer: 0x61800017c980> Path (795A2891-D154-4E52-81F6-7FD79A9A4308)",
 "<MSShapePathLayer: 0x61800017ca40> Path (F1D41D13-370E-4326-823F-A4AAABC80047)",
 "<MSShapeGroup: 0x7f82e075cfe0> Empty (1002B7B5-9CD2-463E-B2A4-4883174074C2)",
 "<MSShapePathLayer: 0x61800017cb00> Path (5577CC1A-9C95-45A7-89E9-AB098B6D0329)",
 "<MSShapePathLayer: 0x61800017cbc0> Path (7628098C-32E7-46E7-A9F6-CBF1E2654653)",
 "<MSShapeGroup: 0x7f82e075d0f0> Empty (86BBDD79-5F5D-4559-8958-002EAF3A353B)",
 "<MSLayerGroup: 0x6180001d3dd0> Signal (5B08C199-0398-45CC-9905-A8B16EFB5B71)",
 "<MSTextLayer: 0x7f82e052e3e0> @constraints (7E04B443-1D85-48FB-B4C6-2334FE7A6408)",
 "<MSArtboardGroup: 0x7f82e074a500> iPhone 6 (8BCE4790-108A-45B3-9F4B-AE4292A8CF27)"
 )
 */

MSArtboardGroup:畫板
MSShapeGroup:組里的圖層都是shape ,比如wifi
MSLayerGroup:比如局域網信號

圖層基類 :
MSLayer
http://developer.sketchapp.com/reference/MSLayer/
https://github.com/abynim/Sketch-Headers/blob/master/Headers/MSLayer.h

文本層
MSTextLayer
https://github.com/abynim/Sketch-Headers/blob/master/Headers/MSTextLayer.h

圖形層:
https://github.com/abynim/Sketch-Headers/blob/master/Headers/MSShapePathLayer.h

MSOvalShape:
MSBitmapLayer:
http://developer.sketchapp.com/reference/MSBitmapLayer/
https://github.com/abynim/Sketch-Headers/blob/master/Headers/MSBitmapLayer.h

實現layer的autoResize屬性

1.1 獲取layer的parentGroup,parent對象可能是MSPage、MSArtboardGroup或MSLayerGroup
1.2 獲得自己的frame,手動計算縮放比例,控制。順便說一句,Sketch有個殘暴的接口:

        layer.frame().scaleBy(radio);

Action

利用Sketch已經實現的一些響應接口,插件少寫代碼,得到的效果更好。

  sendAction: function (commandToPerform) {
      try {
          // target 指定為nil,app會去尋找,響應這個消息的對象。
          [NSApp sendAction:commandToPerform to:nil from:context.document];
      } catch(e) {
          log(e)
      }
  },

在Sketch-Header中可以看到到,Sketch可以響應的 Action,比如:

 MSAlignLayersLeftAction.h      
 - (void)alignLayersLeft:(id)arg1;
MSAlignLayersRightAction.h
- (void)alignLayersRight:(id)arg1;

這些頭文件,都繼承了MSBaseAlignLayersAction。MSBaseAlignLayersAction繼承MSBaseAction。MSBaseAction繼承了NSResponder 0 0呵呵哈。

圖層中存取拓展數據


var doc = context.document;
var selection = context.selection;
var command = context.command;

var currentLayer = (selection.count() > 0) ? selection[0] : false;

var kAutoresizingMask = "kAutoresizingMask";
var content = {
    kAutoresizingFlexibleLeftMargin : "AutoresizingFlexibleLeftMargin",
    kAutoresizingFlexibleRightMargin : "AutoresizingFlexibleRightMargin",
    kAutoresizingFlexibleTopMargin : "AutoresizingFlexibleTopMargin",
    kAutoresizingFlexibleBottomMargin : "kAutoresizingFlexibleTopMargin",
    kAutoresizingFlexibleWidth : "AutoresizingFlexibleWidth",
    kAutoresizingFlexibleHeight : "kAutoresizingFlexibleHeight",
};

var formattedContent = JSON.stringify(content, null, "\t");
command.setValue_forKey_onLayer(formattedContent, kAutoresizingMask, currentLayer);

var constraintsForLayer = command.valueForKey_onLayer(kAutoresizingMask, currentLayer);
log(constraintsForLayer);
log(JSON.stringify(constraintsForLayer, null, "\t"));

附上本周寫的個簡單插件https://github.com/sueLan/Artboard-Mold-For-Sketch

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Swift版本點擊這里歡迎加入QQ群交流: 594119878最新更新日期:18-09-17 About A cu...
    ylgwhyh閱讀 25,573評論 7 249
  • afinalAfinal是一個android的ioc,orm框架 https://github.com/yangf...
    passiontim閱讀 15,569評論 2 45
  • 江南的荷花總是率先一步開的,南方太過熱情,沒那么多時間等待,陽光充足了,花便開了。想來家里的荷花應該剛剛露出苞來,...
    橙汁的汁閱讀 373評論 2 9
  • <前文> 前兩天下午,剛剛出了門,于是下了雨 雨是好東西,只是濕了鞋 如果,你看不到雨,那么,與你聽雨如何 <正題...
    季節性蘇醒閱讀 211評論 0 0
  • 一、為何使用 項目上線后,程序有可能發生崩潰現象,但是開發人員并不知道那個地方出錯了,無法看到崩潰日志。這就需要我...
    V1tas閱讀 1,707評論 2 2