Edited at

Sketch.app Plugin の開発メモ


Sketch Plugin CookBook (ver.51まで)

:warning:Sketch 52でAPIが変更になっているため、動かない記述があります。:warning:

忘れやすい自分のための、Sketch.app向けプラグイン開発メモ。

:innocent: ツギハギで書いてきたため、内容をあまり精査できていません。ところどころ書き換えできずに間違ってます。さらにClass自体が非推奨になった(リファレンスからなくなった)ようで、この書き方は使えなくなる可能性が高いです。「使えなくなる」ではなく、JavaScript API経由で使えってことになりそうです。:innocent:

Sketchのプラグインは、CocoaScirptで開発する。とてもざっくり言うと、Objective-CをJavaScriptライクに扱う言語(キモイ)。記述はJavaScriptスタイルとObjective-Cスタイルがあり、混在もOK。


ドキュメントとか参考情報

クラスリファレンスが見たい場合は、こちらのコミットからローカルへクローンしてJekyllでビルドすれば復元できます。

フォーラム

メーリングリストとそのアーカイブ

こちらも参考に


デバッグ

公式ドキュメント:Debugging — Sketch Developer

公式ドキュメントではフィルタの設定を「sketch」としているが、プラグインのメッセージだけをみたいのであれば、「sketch plugin」とした方が良い、と思う。


Class Dump

class-dumpというコマンドラインツールをインストールすると、メソッドやプロパティを調べることができる。Class-dumpの配布ページからファイルをダウンロードし、中にあるclass-dump/usr/local/binあたりへコピーする。

$ cp /Volumes/class-dump-3.5/class-dump /usr/local/bin/class-dump

ターミナルで次のコマンドを叩けば、プロパディやメソッドを取得できる。

# 含まれるフレームワークすべて

$ class-dump /Applications/Sketch.app

# フレームワークを指定
$ class-dump /Applications/Sketch.app/Contents/Frameworks/CocoaScript.framework

# ファイルとしてホームディレクトリへ書き出す場合
$ class-dump /Applications/Sketch.app > ~/sketch-class-dump.txt

参考記事でも紹介されていたSketch Headersで、同様の情報が閲覧できますので、お好きな方で。


オブジェクトの構成を出力するtreeAsDictionary()

オブジェクトの構造はtreeAsDictionary()を使うとログに吐き出すことができ、プロパティ名からメソッドを憶測できる。

// 選択しているシェイプの構造をログで出力

var sel = context.selection;
var shape = sel[0];
log( shape.frame().treeAsDictionary() );

これを実行すると、次の出力を得ることができる。

{

"<class>" = MSRect;
constrainProportions = 0;
height = 159;
width = 145;
x = 179;
y = 169;
}


クラスやインスタンスが持つメソッドを出力するinstanceMethods()classMethods()

また、class-dumpを紹介したものの、instanceMethods()classMethods()を使うと、そのクラスが持っているメソッドを吐き出すこともできる。

// 選択しているシェイプの構造をログで出力

var sel = context.selection;
var shape = sel[0];
log( shape.frame().class().mocha().instanceMethods() );

これを実行すると、次の出力を得ることができる(

(

"<MOMethodDescription: 0x6100004266c0 : selector=makeOriginIntegral, typeEncoding=v16@0:8>",
"<MOMethodDescription: 0x610000426700 : selector=makeRectIntegral, typeEncoding=v16@0:8>",
// 長いため中略
"<MOMethodDescription: 0x610000428260 : selector=setOrigin:, typeEncoding=v32@0:8{CGPoint=dd}16>",
"<MOMethodDescription: 0x610000428300 : selector=setRect:, typeEncoding=v48@0:8{CGRect={CGPoint=dd}{CGSize=dd}}16>"
)

ちなみにclassMethods()のものはnew()しなくても使える。


プロパティを出力するproperties()

// プロパティのダンプ

var sel = context.selection;
var shape = sel[0];
log( shape.frame().class().mocha().properties() );


Atom向けのプラグイン実行パッケージ

Atomから直接Sketchプラグインを実行できるパッケージ。パッケージマネージャなどでインストールしCommand + Rすると、定義されてているメソッドをパレットから選択して実行できる。

Sketch Scripter

ちなみにAutherはContent Generator Pluginの人。


プラグインの構造

PLUGINNAME.sketchplugin

└ Contents
└ Sketch
├ SCRIPT.sketchscript
└ manifest.json

Pluginsメニューへの表示はmanifest.jsonで管理するため、SCRIPT.sketchscriptは複数あっても良い。拡張子も.js.cocoascriptでも大丈夫。ライブラリなどはSketchディレクトリ内にさらにディレクトリを作成し、その中に突っ込んでもOK。外部のスクリプトを読み込むには、@importを使ってファイルの先頭へ次のように書く。

@import 'lib/script.js';


旧形式のプラグインについて

以前はディレクトリへ実行ファイルのそのものである.sketchpluginを突っ込んでおけば良かったが、3.3から形式が変更になっている。3.4からPreferencesへプラグインマネージャーが実装され、manifest.jsonがないと「Legacy Plugins」として1つにまとめられてプラグインを管理できなくなるので注意。

この旧形式でも41現在、きちんとメンテナンスされていれば実行は可能だが、Legacy PluginはPluginsメニューの別メニューにまとめられてしまう。今後、いつ使えなくなるかわからないため、よく使うプラグインが旧形式なら要注意。

こちらを参照:Plugin News — Sketch Developer


manifest.json

manifest.jsonは、Pluginsメニューやプラグインマネージャーで表示する内容を設定するファイル。下記は、拙作「Sketch CSS Sprite Mixin」のファイルの一部を抽出。こちらはがんばって書いている感じなので、最低限の記述は「Sketch3のプラグインを開発する」を参照のこと。


manifest.json

{

"author": "littlebusters", // プラグインマネージャーで表示
"menu" : {
"isRoot" : false, // Pluginsメニュー直下へ表示するかどうか
"shortcut" : "",
"items" : [ // メニュー上の並びを設定。commands[INDEX].identifierを指定する。3.3では無効
"css-sprite-mixin-scss",
"css-sprite-mixin-sass",
"css-sprite-mixin-less",
"-", // 3.4.1より、ハイフンを入れるとセパレータとして表示されるように
"css-sprite-mixin-stylus"
],
"title" : "Sketch CSS Sprite Mixin" // Pluginsメニューでのフォルダ名
},
"identifier": "net.creative-tweet.css-sprite-mixin",
"version": "1.1.0", // プラグインマネージャーで表示
"description" : "Generate a code of CSS Sprite Mixin to Clipboard in Sketch.", // プラグインマネージャーで表示
"name" : "Sketch CSS Sprite Mixin", // プラグインマネージャー上の名前。指定しない場合は、ファイル名から取得される
"commands" : [
{
"name": "Scss", // Pluginsメニュー上の表示
"identifier": "css-sprite-mixin-scss",
"handler" : "getScss", // 下のscript内にあるメソッドを指定
"script": "css-sprite-mixin.sketchscript" // スクリプトファイルを指定
}
]
}


Global Variable

メソッドにcontextという引数を渡して実行してやる。旧形式でいうdocや選択しているレイヤーを取得するには次の通り。

var onRun = function(context) {

var doc = context.document;
var sel = context.selection;
}


サンプルコード

docとなっているところは、事前にvar doc = context.document;となっている前提とするか、context.documentと読み替えてください。


非推奨になっているmethod


  • MSArray.length() -> MSArray.count() // 3.8

  • setPatternImage() -> SetImage() // 3.8

  • addLayerOfType() -> それぞれのクラスからインスタンスを作成する // 3.8

  • convertToSymbol() -> convertArtboardToSymbol() // 41

  • MSColor -> MSImmutableColor // 41


バージョンの取り方

Bohemian Coding的には最新版しかサポートしない感じですが、とりあえずバージョンによる分岐をする場合に必要なバージョンの取得方法。ちなみに、3.8の次は3.9かと思ったら39になってしまったので、39以降はmajorには二桁の数字が入ります。

var appVer = {}

appVer.full = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
appVer.major = ( appVer.full.split( '.' ) )[0];
appVer.minor = ( appVer.full.split( '.' ) )[1] || 0;
appVer.revision = ( appVer.full.split( '.' ) )[2] || 0;


ページ関連

参考

- Sketch Command/Pages


現在のページを取得

// JS Style

var doc = context.document;
var currentPage = doc.currentPage();

// Obj-c style
var doc = context.document;
var currentPage = [doc currentPage];


ドキュメント内のページ数

// JS Style

var doc = context.document;
var docPages = doc.pages();
log( docPages.count() ); // ページ数を表示

// Obj-c style
var doc = context.document;
var docPages = [doc pages];
log( [docPages count] ); // ページ数を表示


新しいページを追加

// JS Style

var doc = context.document;
doc.addBlankPage();

// Obj-c style
var doc = context.document;
[doc addBlankPage];


現在のページ名を取得

// JS Style

var doc = context.document;
var currentPage = doc.currentPage();
log( currentPage.name() );

// Obj-c style
var doc = context.document;
var currentPage = [doc currentPage];
log( [currentPage name] );


現在のページ名を変更

// JS Style

var doc = context.document;
var currentPage = doc.currentPage();
currentPage.setName( 'Page Name' );

// Obj-c style
var doc = context.document;
var currentPage = doc.currentPage();
[[currentPage] setName:'Page Name'];


アートボード関連


アートボードの一覧を取得

// JS Style

var doc = context.document;
var artboards = doc.currentPage().artboards();
if ( 0 < artboards.count() ) {
for ( var i = 0; i < artboards.count(); i++ ) {
log( artboards[i].name() );
log( artboards[i].frame().width() );
log( artboards[i].frame().height() );
log( artboards[i].frame().x() );
log( artboards[i].frame().y() );
}
}

// Obj-c style
var doc = context.document;
var artboards = [[doc currentPage] artboards];
if ( 0 < [artboards count] ) {
for ( var i = 0; i < [artboards count]; i++ ) {
log( [[artboards objectAtIndex:i] name] );
log( [[[artboards objectAtIndex:i] frame] width] );
log( [[[artboards objectAtIndex:i] frame] height] );
log( [[[artboards objectAtIndex:i] frame] x] );
log( [[[artboards objectAtIndex:i] frame] y] );
}
}


現在のアートボードを取得

// JS Style

var doc = context.document;
var currentArtboard = doc.currentPage().currentArtboard();
if ( 0 > currentArtboard.count() ) {
log( currentArtboard.name() );
}

// Obj-c style
var doc = context.document;
var artboard = [[doc currentPage] currentArtboard];
if ( 0 < [artboards count] ) {
log( [currentArtboard name] );
}


アートボードを作成

// JS Style

var doc = context.document;
var artboard = MSArtboardGroup.new();
artboard.setName( 'Artboard Name' );
artboard.frame().setX( 0 );
artboard.frame().setY( 0 );
artboard.frame().setWidth( 480 );
artboard.frame().setHeight( 720 );
doc.addLayer( artboard );

// Obj-c style
var doc = context.document;
var artboard = [MSArtboardGroup new];
[artboard setName: 'Artboard Name'];
[[artboard frame] setX: 0];
[[artboard frame] setY: 0];
[[artboard frame] setWidth: 480];
[[artboard frame] setHeight: 720];
[doc addLayer: artboard];

複数のアートボードを追加する場合は、doc.addLayer()の部分を次のように変更する。

// JS Style

var currentPage = doc.currentPage();
currentPage.addLayers( NSArray.arrayWithObjects( artboard ) );

// Obj-c Style
var currentPage = [doc currentPage];
[currentPage addLayers:[NSArray arrayWithObjects: artboard]];


現在のアートボードへガイドラインを追加する

var doc = context.document;

var artboard = doc.currentPage().currentArtboard();

var vRuler = artboard.verticalRulerData();
var hRuler = artboard.horizontalRulerData();
vRuler.addGuideWithValue( 100 ); // 垂直
hRuler.addGuideWithValue( 100 ); // 水平


現在のアートボードのグリッドを設定する

var doc = context.document;

var artboard = doc.currentPage().currentArtboard();

var grid = MSSimpleGrid.new();
grid.setGridSize( 16 );
artboard.grid = grid;


レイヤーの作成と設定


グループの作成

var doc = context.document;

var artboard = doc.currentPage().currentArtboard();

var group = MSLayerGroup.new();
group.setName( 'Group Name' );
artboard.addLayers( [group] );


矩形(Rectangle)を作成

var doc = context.document;

var artboard = doc.currentPage().currentArtboard();

var rectangle = MSRectangleShape.new();
rectangle.name = 'Layer Name';
rectangle.frame().x = 0;
rectangle.frame().y = 0;
rectangle.frame().width = 100;
rectangle.frame().height = 150;

var shape = MSShapeGroup.shapeWithPath( rectangle );
artboard.addLayers( [shape] );


ベジェを作成

参考

- Sketch Plugins Cookbook

var doc = context.document;

var artboard = doc.currentPage().currentArtboard();

var bezierPath = NSBezierPath.bezierPath();

bezierPath.moveToPoint( NSMakePoint( 0, 0 ) ); // パスを開始する座標

// 直線を引く
bezierPath.lineToPoint( NSMakePoint( 50, 50 ) ); // アンカーポイントの座標

// 曲線を引く
var ancher = NSMakePoint( 200, 200 ); // アンカーポイントの座標
var currentHandle = NSMakePoint( 150, 200 ); // 作成するアンカーポイントのハンドルの座標
var prevHandle = NSMakePoint( 50, 100 ); // 元のアンカーポイントのハンドルの座標
// 曲線を生成
[bezierPath curveToPoint:ancher controlPoint1:prevHandle controlPoint2:currentHandle];

// パスを閉じる
bezierLine.closePath();

var shape = MSShapeGroup.shapeWithBezierPath( bezierPath );
artboard.addLayers( [shape] ); // ベジェシェイプをアートボードへ追加


マスクの作成

var doc = context.document;

var artboard = doc.currentPage().currentArtboard();

// マスクの影響を抑える為にグループを作成
var group = MSLayerGroup.new();
group.setName( 'Mask Group' );

// グループ内にマスクの矩形を作成

var maskRect = MSRectangleShape.new();
maskRect.name = 'Mask';
maskRect.frame().x = 0;
maskRect.frame().y = 0;
maskRect.frame().width = 100;
maskRect.frame().height = 150;

var mask = MSShapeGroup.shapeWithPath( maskRect );
group.addLayers( [mask] );

// マスク化
mask.setHasClippingMask( true );
// マスク化による親レイヤーの境界や座標を更新


ビットマップを読み込む

var doc = context.document;

var artboard = doc.currentPage().currentArtboard();

// 読み込むファイルのパスを設定
var filePath = '/Users/USERNAME/file/to/path.png';

var image = NSImage.new().initWithContentsOfFile( filePath );
var imageData = MSImageData.alloc().initWithImage_convertColorSpace_(image, true);
var bitmap = MSBitmapLayer.new();
bitmap.image = imageData;
bitmap.name = 'Imported Bitmap';
bitmap.frame().x = 0;
bitmap.frame().y = 0;

artboard.addLayers( [bitmap] );


スライスを作成

var doc = context.document;

var artboard = doc.currentPage().currentArtboard();

var slice = MSSliceLayer.new();
slice.name = 'Slice Name';
slice.frame().x = 0;
slice.frame().y = 0;
slice.frame().width = 100;
slice.frame().height = 150;

artboard.addSlice( slice );


Radiusの設定

// Rectangleツールで作成した矩形を選択した状態で

var sel = context.selection;
var rect = sel.firstObject().layers().firstObject();

rect.cornerRadiusFloat = 10;

// 個別にRadiusを設定する場合は次の通り
// rect.cornerRadiusString = "7/0/0/10";


Blending Modeの設定

// 矩形レイヤー(rectangle)を作成したのち

var contextSettings = rectangle.style().contextSettings();
contextSettings.blendMode = 1;

Blend Modeの数値は、こちらを参照のこと


レイヤー自体のOpacity設定

// Blending Modeの設定のコードの後に

contextSettings.opacity = 0.5; // 0から1の値


スタイリング


Fill(Flat Color)を設定

// 矩形レイヤー(rectangle)を作成したのち

// Fillを追加してプロパティを取得
var fill = rectangle.style().addStylePartOfType( 0 );
// Fillに適用するカラーを設定
var color = MSImmutableColor.colorWithSVGString( '#ff0000' );
color.alpha = 0.8;
// Fillにカラーを適用
fill.color = color;

addStylePartOfType()した場合、デフォルトとしてFlat Colorが適用される。Gradientから変更する場合は、fill.setFillType( 0 )とする。


Fill(Gradient)を設定

// 矩形レイヤー(rectangle)を作成したのち

// Fillを追加してプロパティを取得
var fill = rectangle.style().addStylePartOfType( 0 );

// Fillの種類をGradientに変更
fill.setFillType( 1 );
// Gradientプロパティを取得
var gradient = fill.gradient();
// Gradient Typeの設定 / 0: Linear / 1: Radial / 2: Angular
gradient.setGradientType( 0 );
// Gradient Stopを追加 / value: 0..1
gradient.addStopAtLength( 0.5 );
// Gradient Stopに適用するカラーと透明度を設定
var gradientColor = MSImmutableColor.colorWithSVGString( '#ff0000' );
gradientColor.alpha = 0.8;
// Gradient Stopにカラーを適用
gradient.setColor_atIndex_( gradientColor, 1 );
// Gradient Stopの位置を設定
gradient.setPoint_atIndex_( CGPointMake( 1, 1 ), 1 );

Gradient Stopの位置を設定するCGPointMake()は、レイヤーの大きさに対する割合をX・Yで指定する。

例えばW100×H100のレイヤーで、CGPointMake( 0.4, 0.5 )と指定すれば、オブジェクトの左上起点でX40/Y50の位置へGradient Stopが移動する。ただし、atIndexを両端以外で指定している場合、両端を結ぶ直線上の範囲で移動が行われる。


Fill(Pattern)を設定

// 矩形レイヤー(rectangle)を作成したのち

// Fillを追加してプロパティを取得
var fill = rectangle.style().addStylePartOfType( 0 );

// Fillの種類をPatternに変更
fill.setFillType( 4 );
// 整列方法を設定 / 0: Tile / Fill: 1
fill.setPatternFillType( 1 );

// パターン画像のパスから画像データを作ってFillへ設定する
var imageData = MSImageData.alloc().initWithImage_convertColorSpace_( '/file/to/path/document.png', false );
fill.setImage( imageData );


Fill(Noise)を設定

// 矩形レイヤー(rectangle)を作成したものとする

// Fillを追加してプロパティを取得
var fill = rectangle.style().addStylePartOfType( 0 );

// Fillの種類をNoiseに変更
fill.fillType = 5; // fill.setFillType( 5 )でもOK
// ノイズの種類を指定 / 0: Original / 1: Black / 2: White / 3: Color
fill.noiseIndex = 3;
// IntensityはOpacityとして設定する
fill.contextSettings().opacity = 0.5;


ドロップシャドウ / インナーシャドウ

// 矩形 rectangle を作成済みとして

// 2: ドロップシャドウ / 3: インナーシャドウ
var effect = addStylePart( rectangle, 2 );

// ぼかしの大きさ
effect.blurRadius = 10;

// XY座標
effect.offsetX = 0;
effect.offsetY = 0;

// スプレッドの大きさ
effect.spread = 0;

// 色と不透明度
var color = MSImmutableColor.colorWithSVGString( getHexColor( attrs[i].ShadowColor ) );
color.alpha = getOpacity( attrs[i].ShadowColor );
effect.color = color;


ぼかし各種

// 矩形レイヤー(rectangle)を作成したものとする

var effect = rectangle.style().blur();

// ぼかし効果を有効化
effect.isEnabled = 1;

// 0: ガウス / 1: モーション / 2: ズーム / 3: バックグラウンド(多分)
effect.type = 2;

// ぼかしの大きさ
effect.radius = 10;


テキストレイヤーの作成

var doc = context.document;

var artboard = doc.currentPage().currentArtboard();

var text = MSTextLayer.new();

// 0: Auto(デフォルト) / 1: テキストボックスの幅を固定
text.textBehaviour = 1;

// テキストに文字列を入れる
text.stringValue = 'テキストの内容だよ';

// フォントサイズの設定
text.fontSize = '24';

// フォントの設定(Postscript名で指定する)
text.fontPostscriptName = 'フォント名';

// テキストカラーの設定
text.textColor = MSImmutableColor.colorWithSVGString( '#ff0000' );

// テキストボックスの大きさを最適化
text.adjustFrameToFit();

// パラグラフを設定する
var pragraph = NSMutableParagraphStyle.new();
pragraph.minimumLineHeight = 12;
pragraph.maximumLineHeight = 24;
text.addAttribute_value_( NSParagraphStyleAttributeName, pragraph );

// テキストの揃え - 0: 左 / 1: 右 / 2: 中央
text.setTextAlignment( 0 );

// サイズや位置、レイヤー名なども設定できます
artboard.addLayers( [text] );


文字の一部の設定を変更する

// 変更する範囲を作成(0文字から2文字目まで)

var range = NSMakeRange( 0, 2 );

// フォント名をサイズを指定
var textFont = NSFont.fontWithName_size_( 'フォント名(Postscript)', 24 );

// 色を指定
var color = MSImmutableColor.colorWithSVGString( '#ff0000' );

// 一部の変更を実行
text.setIsEditingText( true );
text.addAttribute_value_forRange_( NSFontAttributeName, textFont, range );
text.addAttribute_value_forRange_( NSForegroundColorAttributeName, color, range );
text.setIsEditingText( false );

// パラグラフも設定できますが、多分一部だけを変えることはないでしょうね
text.addAttribute_value_forRange_( NSParagraphStyleAttributeName, pragraph, range );


Symbol


アートボードからシンボルを作成する

あらかじめSymbolにするアートボードを作成しておくこと。

MSSymbolMaster.convertArtboardToSymbol( artboard );

// artboard = あらかじめ作成していたアートボード

ただこれではインスタンスを作成しづらいので、オブジェクトに放り込んでおくと良い…かも?

var symbols['symbolName'] = MSSymbolMaster.convertArtboardToSymbol( artboard );


Symbol Instanceの作成

var doc = context.document;

var cp = doc.currentPage();
for( var i = 0; i < doc.pages().count(); i++ ) {
if( 'Symbols' == doc.pages()[i].name() ) {
var symbolsPage = doc.pages()[i];
break;
}
}

var symbolInstance = symbolsPage.artboards()[0].newSymbolInstance();
symbolInstance.setName( 'Symbol Instance' );
symbolInstance.frame().x = 0;
symbolInstance.frame().y = 100;
cp.addLayers( [symbolInstance] );


メッセージ・ダイアログ関連

参考

- User Input & Feedback


ウィンドウ下部へメッセージ

// JS Style

doc.showMessage( 'Message' );

// Obj-c style
[doc showMessage: 'Meaasage'];


Cocoaでモーダルダイアログ

アイコンとかも設定できるはず。

// JS Style

var userInput = COSAlertWindow.new();

//ダイアログタイトル
userInput.setMessageText( 'Dialog Title' );

// テキストフィールド
userInput.addTextLabelWithValue( 'Text Field Title' );
userInput.addTextFieldWithValue( 'Text Field Placeholder' );

// チェックボックス
var checkbox = NSButton.alloc().initWithFrame( NSMakeRect( 0, 0, 300, 18 ) );
checkbox.setButtonType( NSSwitchButton );
checkbox.setTitle( 'Switch Button Title' );
checkbox.setTag( 'value' );
checkbox.setState( false );
userInput.addAccessoryView( checkbox );

// ラジオボタン
var radiobutton = NSButton.alloc().initWithFrame( NSMakeRect( 0, 0, 300, 18 ) );
radiobutton.setButtonType( NSRadioButton );
radiobutton.setTitle( 'Radio Button Title' );
radiobutton.setTag( 'value' );
radiobutton.setState( true );
userInput.addAccessoryView( radiobutton );

// ボタン
userInput.addButtonWithTitle('OK');

// 実行
userInput.runModal();

// Obj-c style
var userInput = [COSAlertWindow new];

//ダイアログタイトル
[userInput setMessageText:'Dialog Title'];

// テキストフィールド
[userInput addTextLabelWithValue:'Text Field Title'];
[userInput addTextFieldWithValue:'Text Field Placeholder'];

// チェックボックス
var checkbox = [[NSButton alloc] initWithFrame:NSMakeRect( 0, 0, 300, 18 )];
[checkbox setButtonType:NSSwitchButton];
[checkbox setTitle: 'Switch Button Title'];
[checkbox setTag: 'value'];
[checkbox setState: false];
[userInput addAccessoryView: checkbox];

// ラジオボタン
var radiobutton = [[NSButton alloc] initWithFrame:NSMakeRect( 0, 0, 300, 18 )];
[radiobutton setButtonType: NSRadioButton];
[radiobutton setTitle: 'Radio Button Title'];
[radiobutton setTag: 'value'];
[radiobutton setState: true];
[userInput addAccessoryView: radiobutton];

// ボタン
[userInput addButtonWithTitle: 'OK'];

// 実行
[userInput runModal];

setButtonTypeはこのあたりを参照


Fw.jsf でいう、prompot();

// JS Style

var userInput = doc.askForUserInput_initialValue( 'Message', 'Default Value' );

// Obj-c style
var position = [doc askForUserInput: 'Message' initialValue: 'Default Value'];

JavaScript スタイルの書き方が分からなかったが、「_」でつなぐと良いことを教わった。


JSONファイルを選択してパース

ただし、runModalForDirectoryが10.6で非推奨になっているので、他の方法を探す(10.10でも一応動作可能)。

var openPanel = NSOpenPanel.openPanel();

openPanel.setTitle( "Choose a JSON File" ); // ダイアログのタイトル
openPanel.setCanCreateDirectories = false; // ディレクトリの作成を許可するか
openPanel.setCanChooseFiles = true; // ファイル選択を許可するか

var fileTypes = ['json']; // 選択できるファイルタイプを設定
var openPanelButtonPressed = openPanel.runModalForDirectory_file_types_( nil, nil, fileTypes );

if ( openPanelButtonPressed == NSFileHandlingPanelOKButton ) {
var filePath = openPanel.URL().path();
var json = JSON.parse( NSString.stringWithContentsOfFile( filePath ) );
log( 'Open File from: ' + filePath );
} else {
return false;
}


そのほか


フォントの一覧を取得

var fontList = NSFontManager.sharedFontManager().availableFontFamilies();


フォントファミリーを取得

var fontFamilies = NSFontManager.sharedFontManager().availableMembersOfFontFamily('Helvetica Neue');

引数として、フォント名を渡す。PostScript名はNGで、「フォントの一覧」で取得したフォント名を使うこと。


ArrayからJSONに

例えば上記の「フォントの一覧」から作成する場合

var fontList = NSFontManager.sharedFontManager().availableFontFamilies();

var data = NSJSONSerialization.dataWithJSONObject_options_error_(fontList, 0, nil);
var json = NSString.alloc().initWithData_encoding_(data, 4);


Plugins Directoryへの登録

オリジナルドキュメント:Contributing

GitHubやウェブサイト等へアクセスし、ダウンロードできるように準備しておく。



  1. Sketch Plugins DirectoryのリポジトリをForkし、ローカルへCloneする。


  2. plugins.jsonを編集する。


    1. Object({}の括り)をコピペする。

    2. それぞれの項目を英語で埋める。Google翻訳したような英語でも問題なさそう。

      最低限、description(プラグインの説明)・name(リポジトリ名)・title(プラグイン名)・owner(リポジトリの所有者名)を埋める。

      そのほかの情報として、owner(作者名 リポジトリ所有者名以外を使う場合)・homepage(リポジトリ以外でプラグインを配布している場合のリンク先)・lastUpdated(最終更新日)がある。

    3. 不安なら、適当なJSON linterを通しておく。



  3. Terminal.appで、Cloneしたディレクトリへ移動し、rakeコマンドを実行すると、plugins.jsonに合わせて、README.mdが更新される。

  4. リモートのリポジトリへプッシュし、元のリポジトリへPull Requestを送る。メッセージは何でもOK。