はじめに
そんなに真面目じゃないネタ記事です。
今回はタイトルのようなSketch Pluginを作ることでレイヤー操作や調査方法について学んでいきます。
作るプラグイン
実行するとレイヤーの背景をマル禁画像に書き換えます。
以下は実際に実行したもの(今回の完成品)になります。
普通のレイヤーやシンボル、またレイヤー名も🈲に変わります。
🈲だとバカっぽいですが画像を「Confidential(機密)」にすればそれっぽい感じになりそうです。
注意事項
- これは開発練習用のプラグインです。
- 大切なSketchファイルでは実行しないでください。
- 使用者の責任でご使用ください。
- 実行してもUndoすれば戻せます。
なお以下では、「レイヤーを🈲に変える」という表記が面倒なので**「Kinする」**という独自表記にさせていただきます。
ソース
以下に置いてあります。画像ファイルもあります。
https://github.com/hanamiju/SketchConfidentialPlugin
なおこのエントリーでは細かくsketch pluginの作り方は示しません。公式ドキュメント等をご覧ください。
今回いじるファイルは以下のファイルのみです。
Confidential.sketchplugin/Contents/Sketch/script.cocoascript
開発
画像を読み込む
まずはKin画像を読み込みます。
Cocoaの心得が少しあれば普通に理解できると思います。
ソース
var plugin = context.plugin
var imageFilePath = plugin.urlForResourceNamed("kin.png") // 画像のURL取得
var imageData = [NSData dataWithContentsOfURL:imageFilePath] // データに起こす
var image = NSImage.alloc().initWithData(imageData) // imageオブジェクトにする
解説
今回の.sketchpluginファイルは以下の構成になっています。
Confidential.sketchplugin
└ Contents
├ Sketch
│ ├ script.sketchscript
│ └ manifest.json
│
└ Resource
└ kin.png
urlForResourceNamed()
でResourseフォルダのファイル名を指定すると画像のURL取得が取得できるのでこれをもとに画像をオブジェクト化します。
選択したShapeLayerをKinする
選択したレイヤーを取得してKinをしていきます。
Sketchの操作でいう、レイヤーを選択してFillの方法をImageにしてKin.pngを入れるのと同じことをします。
ソース
var onRun = function(context) {
var plugin = context.plugin
var imageFilePath = plugin.urlForResourceNamed("kin.png")
var imageData = NSData.dataWithContentsOfURL(imageFilePath)
var image = NSImage.alloc().initWithData(imageData)
changeLayers(context, image)
};
function changeLayers(context, image) {
var selections = context.selection // 選択したレイヤー
// 選択したレイヤー分だけKinする
change(selections, image)
}
function change(layers, image) {
for(var i = 0; i < layers.length; i++){
var layer = layers[i];
if(layer.class() == MSShapeGroup){
var fill = layer.style().fills().firstObject();
if (fill != nil) {
fill.setFillType(4); // 塗りつぶし方法(画像)
fill.setImage(MSImageData.alloc().initWithImage_convertColorSpace(image, false));
fill.setPatternFillType(3); // 塗りつぶしパターン(レイヤーにFit)
}
}
}
}
解説
changeLayers関数
context.selection
で現在選択されているレイヤーが取得できます。
change関数
レイヤーはMSShapeGroup
というクラスなのでレイヤーにこいつが入っていたらKinしていきます。
ちなみに、ソースのコメントに書いてある塗りつぶし情報/塗りつぶしパターンはSketch.appでいう以下の選択項目です。
結果
レイヤー単体ではKinできましたが、グループレイヤーには適用できていないようです。
選択したグループをKinする
グループにもKinを対応させていきます。
ソース
// onRun(), changeLayers()の記載は省略
function change(layers, image) {
for(var i = 0; i < layers.length; i++){
var layer = layers[i];
if(layer.class() == MSShapeGroup){
var fill = layer.style().fills().firstObject();
if (fill != nil) {
fill.setFillType(4);
fill.setImage(MSImageData.alloc().initWithImage_convertColorSpace(image, false));
fill.setPatternFillType(3);
}
// 追加
} else if (isGroup(layer)) {
change(layer.layers(), image)
}
}
}
// 追加
var isGroup = function(layer){
return layer.isMemberOfClass(MSLayerGroup.class())
};
解説
isGroup
でレイヤーがグループ(MSLayerGroup)かどうかを判別する処理を実装します。
MSLayerGroupオブジェクトはプロパティにlayers
という子レイヤーを持っているのでこれをまたchange
関数に投げます。
結果
グループにもKinできましたが、まだうまく動いていないようです。
動いていないのはシンボルです。シンボルはgroupとは別のやり方でKinしてあげないといけなそうです。
選択したシンボルをKinする
次はレイヤーがシンボルだった時の処理を書きたいです。
シンボルはグループとは構成が違うようで子レイヤーのプロパティが見当たりませんでした。
しょうがないのでシンボルをグループに強制的に戻します。
そして、グループレイヤーに対してKinします。
ソース
// onRun(), changeLayers(), isGroupの記載は省略
function change(layers, image) {
for(var i = 0; i < layers.length; i++){
var layer = layers[i];
if(layer.class() == MSShapeGroup){
var fill = layer.style().fills().firstObject();
if (fill != nil) {
fill.setFillType(4);
fill.setImage(MSImageData.alloc().initWithImage_convertColorSpace(image, false));
fill.setPatternFillType(3);
}
} else if (isGroup(layer)) {
change(layer.layers(), image)
// 追加
} else if (isSymbol(layer)) {
changeSymbolInstance(layer, image)
}
}
}
// 追加
function changeSymbolInstance(symbolInstance, image) {
// シンボルをグループレイヤーに直す
var layer = symbolInstance.detachByReplacingWithGroup()
change(layer.layers(), image)
}
// 追加
var isSymbol = function(layer){
return layer.isMemberOfClass(MSSymbolInstance.class())
};
解説
シンボルはMSSymbolInstanceクラスなのでisSymbol
でレイヤーがMSSymbolInstanceクラスか判断します。
そしてMSSymbolInstance.detachByReplacingWithGroup()
でシンボルをグループに戻します。
グループに直したものをchange
関数でKinします。
結果
これでシンボルも無理矢理Kinできました。
いい感じになってきたのでテキストもKinにしていきましょう。
選択したTextLayerをKinする
テキストは画像に変えられないので、文字を「🈲」に変えてしまいましょう。
ソース
function change(layers, image) {
for(var i = 0; i < layers.length; i++){
var layer = layers[i];
if(layer.class() == MSShapeGroup){
var fill = layer.style().fills().firstObject();
if (fill != nil) {
fill.setFillType(4);
fill.setImage(MSImageData.alloc().initWithImage_convertColorSpace(image, false));
fill.setPatternFillType(3);
}
// 追加
} else if (layer.class() == MSTextLayer) {
layer.stringValue = "🈲"
} else if (isGroup(layer)) {
change(layer.layers(), image)
} else if (isSymbol(layer)) {
changeSymbolInstance(layer, image)
}
}
}
解説
テキストレイヤーのクラスはMSTextLayerです。こいつがレイヤーできたらstringValue
プロパティにKinします。
結果
テキストもKinできました。
ここまでくるとサイドバーのレイヤー名もKinしたいところです。
選択したレイヤー名をKinする
レイヤー名もテキストと同じく文字を「🈲」に変えます。
ソース
function change(layers, image) {
for(var i = 0; i < layers.length; i++){
var layer = layers[i];
// 追加
layer.setName("🈲")
if(layer.class() == MSShapeGroup){
var fill = layer.style().fills().firstObject();
if (fill != nil) {
fill.setFillType(4);
fill.setImage(MSImageData.alloc().initWithImage_convertColorSpace(image, false));
fill.setPatternFillType(3);
}
} else if (layer.class() == MSTextLayer) {
layer.stringValue = "🈲"
} else if (isGroup(layer)) {
change(layer.layers(), image)
} else if (isSymbol(layer)) {
changeSymbolInstance(layer, image)
}
}
}
解説
レイヤーのsetName()
メソッドにKinをするだけです。
結果
ここまでくるといよいよ狂気じみてきました。
今まではわざわざ選択したレイヤーに対してのみKinしてきましたが、いちいち選択するのもかったりいので次はpage内の全レイヤーを強制的にKinしていきます。
選択していないLayerもKinする
現在のpageのすべてのlayerが取れるのでこれをKinすればいちいち選択することもなさそうです。
ソース
function changeLayers(context, image) {
var doc = context.document
// 変更
var layers = doc.currentPage().layers()
change(layers, image)
}
解説
なし
結果
壮観ですね。
宿題
他にも色々Kinしてみたい感じではありますが、これより先は僭越ながらここまでお読みいただいた方への宿題とさせていただきたいです。
- SketchファイルすべてのpageをKinする: 10点
- ShapeLayerのBorderを全て赤に変える: 5点
- 画像Layerがあったら、画像Layer削除して同じ位置に同じサイズのLayerを作り、それをKinする: 30点
- Undoできなくさせる(UndoManagerを見つけ出してUndoをリセットすればできるはず...): 50点
↑のいずれかでもできましたらプルリクいただけると嬉しいです。
プロパティ/メソッドの調査方法
Sketchの独自クラスを駆使していかないと、Kinがうまくできないのですが、公式リファレンスを見てもあまり詳しい情報が載っていません。
そこでSketch.appのヘッダーを見て良さそうなプロパティ/メソッドを探します。
class-dumpする方法もありますが、私は面倒なのでSketch-Headersにお世話になっています。
Sketch-Headersはclass-dumpしたヘッダーファイル群のリポジトリです。
割と頻繁に更新されています。
例
シンボルをKinした時に必要だったMSSymbolInstance.detachByReplacingWithGroup()
をどう調べたかの例です。
レイヤーのクラス名を調べる
最初の時点ではMSSymbolInstanceクラスがあることも知りません。
まずKinできなかったレイヤーのクラス名を調べます。
普通に以下のようにログを仕掛けてプラグインを実行します。
log(layer)
コンソールを確認すると以下のように表示されます。
2016/12/17 18:11:02.025 Confidential (Sketch Plugin)[2158]: <MSSymbolInstance: 0x7f9a81041c00> hoge (BAFD957A-AAE2-4D58-B330-792B6CEECEB7)
ここでMSSymbolInstanceクラスの存在を知ります。
Sketch-Headersを見る
クラス名がわかったのでSketch-Headerを見ます。
ここで子レイヤーの情報あるかなとか、それっぽい関数ないかなと調べて色々試します。
その結果、今回はdetachByReplacingWithGroup()
使ってシンボルをぶっ壊そうという結論になりました。
むすび
Kinできるレイヤーが増えるということは、あなたがPluginで操作できる領域が増えるということです。ぜひ、あらゆるSketchファイルをKinしてPlugin力を上げていってください。