В этом посте собрана коллекция рецептов для разработчиков плагинов под Sketch, а также небольшой мануал по созданию плагинов для Sketch.

Руководство новичка по разработке Sketch-плагинов

CocoaScript: Не используйте оператор '==='

На первый взгляд, CocoaScript кажется просто вымышленным именем JavaScript с небольшими изменениями синтаксиса, но в реальности действительно много вещей работает по-другому, и лучше знать эти отличия.

Справочник по созданию плагинов для Sketch

Одним из отличий являются операторы === и ≠= больше известные как строгое равенство и строгое неравенство. И вот мой краткий совет по поводу них:

ЛЮБОЙ ЦЕНОЙ СТАРАЙТЕСЬ НЕ ИСПОЛЬЗОВАТЬ ИХ В ПЛАГИНАХ SKETCH!

Чтобы понять суть проблемы, попробуйте запустить этот скрипт:

var strA = «hello!»; var strB = @"hello!";
if (strA == strB) {
print («They are EQUAL!»);
} else {
print («NOT EQUAL!»)
}
// -> «They are EQUAL!»
if (strA === strB) {
print («They are EQUAL!»);
} else {
print («NOT EQUAL!»)
}
// -> «NOT EQUAL!»

Вам выдаст true для оператора == и false для ===. Значения строк одинаковы, обеим назначена строка «hello!», но у них разные типы. А теперь запустите скрипт, чтобы проверить их типы:

function typeOf (obj) {
print (toString.call (obj));
}
var strA = «hello!»;
var strB = @"hello!";
typeOf (strA);
// -> [object String]
typeOf (strB);
// -> [object MOBoxedObject]

Как видите, переменные strA и strB являются разного типа. strA — это строка JavaScript, но strB — это загадочная переменная MOBoxedObject. Проблема в определении strB — @"hello!" идентична NSString.stringWithString («hello!»), выдает сущность класса NSString вместо строки JS.

При разработке плагинов для Sketch, вы обычно имеете дело с данными, производимыми на стороне Sketch Runtime. И большинство методов классов и получателей свойств возвращают запакованные объекты Objective-C вместо родных JS-объектов.

Чтобы продемонстрировать проблему на реальном примере, вы можете: (1) Создать прямоугольник, (2) Выделить его, (3) Запустить следующий скрипт:

var layer=selection.firstObject ();
if (layer) {
print (layer.name ());
// -> Rectangle 1
var isNameEqual = layer.name () === «Rectangle 1″;
print (isNameEqual);
// -> false
}

Примечание: Использование === и ≠= не запрещено, вы можете использовать их, когда вам захочется, но всегда обращайте внимание на типы сравниваемых переменных. Это особенно важно, когда вы пытаетесь портировать существующую JavaScript-библиотеку или фреймворк на CocoaScript. Но, в любом случае, я настаиваю на том, чтобы вы забыли про операторы равенства/неравенства и использовали '==' и '' + ручную проверку типа, если это нужно.

Проигрывание звука в плагинах sketch

Обычно звуки, привязанные к командам, — бесполезны и только напрягают, но при правильном использовании они могут быть очень полезны.

Проигрывание звука в плагинах sketch
С тех пор, как у плагинов Sketch появился доступ ко всем API из AppKit Framework, мы получили возможность делать очень крутые штуки с плагинами, например, проиграть звук bump!, когда плагин показывает уведомление об ошибке, используя -MSDocument.displayMessage: это метод сделать уведомление более заметным для пользователя.

Чтобы проиграть звук, мы можем использовать простой интерфейс класса NSSound. Вот пример использования:
var filePath = sketch.scriptPath.stringByDeletingLastPathComponent ()+"/assets/glass.aiff»;
var sound = NSSound. alloc ().initWithContentsOfFile_byReference (filePath, true);
doc.displayMessage («I'm Mr Meeseeks LOOK AT ME! :)»)
sound.play ();

ВАЖНОЕ ПРИМЕЧАНИЕ: Если нужно проиграть аудио-файлы, хранящиеся вне папки плагина в MAS-версии Sketch, вам придется использовать библиотеку sketch-sandbox для авторизации доступа к файлам, так как эта версия Sketch помещена в песочницу и запрещает доступ к файлам, находящимся вне песочницы.

Готовые примеры:

Работает в:

  • Sketch 3.0 +

Отцентрировать прямоугольник на холсте

Чтобы отцентрировать холст в определенной точке или области, вы можете воспользоваться удобным методом (void)MSContentDrawView.centerRect:(GKRect*)rect animated:(BOOL)animated, где rect — это прямоугольник, который нужно отцентрировать, а animated — это флажок, который включает/выключает анимацию во время скрола.

Отцентрировать прямоугольник на холсте

Координаты и размер прямоугольника, передаваемые этому методу, должны быть в абсолютных координатах.

Следующий пример центрирует область просмотра относительно точки x:200,y:200:
var view=doc.currentView ();
var rect=GKRect.rectWithRect (NSMakeRect (200,200,1,1));
view.centerRect_animated (rect, true);
Пример ниже показывает, как отцентрировать первый выделенный слой, используя тот же метод:
var layer = selection. firstObject ();
if (layer) {
var view=doc.currentView ();
view.centerRect_animated (layer.absoluteRect (), true);
}

Работает в:

  • Sketch 3.1 +

Создание произвольной фигуры

Чтобы создать произвольную векторную фигуру программным путем, нужно создать сущность класса NSBezierPath и нарисовать любую фигуру или комбинацию фигур, которая вам нужна. Затем из этого создайте группу фигур, используя метод класса +(MSShapeGroup*)MSShapeGroup.shapeWithBezierPath:(NSBezierPath*)path.

Создание произвольной фигуры

Этот прием очень похож на создание произвольных кривых, описанных в предыдущем рецепте. Единственная разница в том, что нужно замкнуть контур перед конвертацией в фигуру.

Этот пример создает простую фигуру стрелы:
var doc = context. document;
var path = NSBezierPath. bezierPath ();
path.moveToPoint (NSMakePoint (10,10));
path.lineToPoint (NSMakePoint (100,10));
path.lineToPoint (NSMakePoint (100,0));
path.lineToPoint (NSMakePoint (120,15));
path.lineToPoint (NSMakePoint (100,30));
path.lineToPoint (NSMakePoint (100,20));
path.lineToPoint (NSMakePoint (10,20));
path.closePath ();
var shape = MSShapeGroup. shapeWithBezierPath (path);
var fill = shape. style ().fills ().addNewStylePart ();
fill.color = MSColor. colorWithSVGString («#dd0000»);
doc.currentPage ().addLayers ([shape]);
Законченные примеры:

  • Create Custom Shape. sketchplugin

Работает в:

  • Sketch 3.2 +

Создание линейной фигуры

Чтобы программно создать линейную фигуру, нужно создать сущность класса
NSBezierPath и добавить на нее две точки. Затем создайте группу фигуры, используя метод класса +(MSShapeGroup*)MSShapeGroup.shapeWithBezierPath:(NSBezierPath*)path.

Создание линейной фигуры

Чтобы Sketch распознал предоставленную линейную фигуру, нужно добавить всего две точки, используя методы moveToPoint и lineToPoint из NSBezierPath.

Следующий пример создает простую линейную фигуру с двумя точками:

var doc = context. document;
var path = NSBezierPath. bezierPath ();
path.moveToPoint (NSMakePoint (10,10));
path.lineToPoint (NSMakePoint (200,200));
var shape = MSShapeGroup. shapeWithBezierPath (path);
var border = shape. style ().borders ().addNewStylePart ();
border.color = MSColor. colorWithSVGString («#dd0000»);
border.thickness = 2;
doc.currentPage ().addLayers ([shape]);

Таким же образом вы можете создать мультисегментную кривую, используя методы класса NSBezierPath. Когда добавляете больше, чем две точки, на кривую, Sketch расценивает такую фигуру, как векторную кривую, какую можно создать и стандартными инструментом V — Vector.

Этот пример демонстрирует, как создать кривую с четырьмя точками:

var doc = context. document;
var path = NSBezierPath. bezierPath ();
path.moveToPoint (NSMakePoint (84.5,161));
[path curveToPoint: NSMakePoint (166,79.5) controlPoint1: NSMakePoint (129.5,161) controlPoint2: NSMakePoint (166,124.5)];
[path curveToPoint: NSMakePoint (84.5,-2) controlPoint1: NSMakePoint (166,34.5) controlPoint2: NSMakePoint (129.5,-2)];
[path curveToPoint: NSMakePoint (3,79.5) controlPoint1: NSMakePoint (39.5,-2) controlPoint2: NSMakePoint (3,34.5)];
var shape = MSShapeGroup. shapeWithBezierPath (path);
var border = shape. style ().borders ().addNewStylePart ();
border.color = MSColor. colorWithSVGString («#dd0000»);
border.thickness = 2;
doc.currentPage ().addLayers ([shape]);

Готовые примеры:

Работает в:

  • Sketch 3.2 +

Установка радиуса границы для отдельных углов

Начиная с версии 3.2, Sketch позволяет установить произвольный радиус границы для каждого отдельного угла прямоугольника. Это было возможно и в версиях раньше 3.2, но не было прямой возможности в API.

Установка радиуса границы для отдельных углов

Чтобы установить произвольный радиус, нужно использовать метод сущности
MSRectangleShape.setCornerRadiusFromComponents:(NSString*)compoents, где components — это строка, которая представляет значения радиуса для каждого угла, отделенные символами /. Последовательность такова: левый-верхний/правый-верхний/правый-нижний/левый-нижний.

Следующий пример настраивает радиус левого и правого верхнего углов выделенного прямоугольника на 15 точек:

var selection = context. selection;
var layer = selection. firstObject ();
if (layer && layer. isKindOfClass (MSShapeGroup)) {

var shape=layer.layers ().firstObject ();
if (shape && shape. isKindOfClass (MSRectangleShape)) {

shape.setCornerRadiusFromComponents («15/15/0/0»);

}

}

Готовый пример:

Работает в:

  • Sketch 3.2 +

Масштабирование слоев

Вы можете масштабировать любо слой, используя метод сущностей MSLayer. multiplyBy:(double)scaleFactor, где scaleFactor — это значение с плавающей запятой, используемое для умножения всех свойств слоя, включая позицию, размер и все атрибуты стиля, такие как толщина границ, тень и т. д. Вот некоторые примеры значений масштаба: 1.0 = 100%, 2.5 = 250%, 0.5 = 50% и т.д.

Этот метод выдает тот же результат, что и стандартный инструмент Scale в Sketch. Так как все классы типа слоя наследуются из класса MSLayer, вы можете использовать этот метод для масштабирования любого слоя, включая страницы и артборды.

Примечание: после вызова метода, значения позиций x и y также будут умножены. Если вам нужно, чтобы слой оставался на прежней позиции после масштабирования, вам придется изменить его позицию на соответствующие значения.

Масштабирование слоев

Этот пример демонстрирует масштабирование первого выделенного слоя:
var selection = context. selection;
var layer = selection. firstObject ();
if (layer) {
// Preserve layer center point.
var midX=layer.frame ().midX ();
var midY=layer.frame ().midY ();
// Scale layer by 200%
layer.multiplyBy (2.0);
// Translate frame to the original center point.
layer.frame ().midX = midX;
layer.frame ().midY = midY;
}

Работает в:

  • Sketch 3.1 +

Поиск границ для набора слоев

Если вы хотите быстро вычислить граничный прямоугольник для выделенных слоев или набора слоев, есть очень удобный метод класса: +(CGRect)MSLayerGroup.groupBoundsForLayers:(NSArray*)layers. Он принимает список слоев и возвращает структуру CGRect.

Поиск границ для набора слоев
Быстрый пример, демонстрирующий, как использовать этот способ:

var selection = context. selection;
var bounds=MSLayerGroup.groupBoundsForLayers (selection);
print («x: «+bounds.origin.x);
print («y: «+bounds.origin.y);
print («width: «+bounds.size.width);
print («height: «+bounds.size.height);
[/dt_code]
Работает в:

  • Sketch 3.3 +

Создание овальной фигуры

Чтобы программным путем создать овал, нужно создать сущность MSOvalShapeclass, настроить рамку и обернуть в контейнер MSShapeGroup.

Создание овальной фигуры

Вот пример, как это сделать:

var doc = context. document;
var ovalShape = MSOvalShape. alloc ().init ();
ovalShape.frame = MSRect. rectWithRect (NSMakeRect (0,0,100,100));
var shapeGroup=MSShapeGroup.shapeWithPath (ovalShape);
var fill = shapeGroup. style ().fills ().addNewStylePart ();
fill.color = MSColor. colorWithSVGString («#dd2020»);
doc.currentPage ().addLayers ([shapeGroup]);

Готовый пример:

Работает в:

  • Sketch 3.1 +

Создать общий стиль программным путем

Чтобы создать общий стиль программным путем, используйте метод -MSSharedLayerStyleContainer.addSharedStyleWithName:(NSString*)name, где name — это название создаваемого стиля, а style — это стиль, взятый в качестве шаблона для будущего общего стиля.

Вы можете создать общий стиль из уже существующего, привязанного к определенному слою, или создать такой стиль с нуля, используя сущность MSStyle.

Создать общий стиль программным путем

Создание общего стиля из стиля выделенных слоев:

var selection = context. selection;
var doc = context. document;
var layer=selection.firstObject ();
if (layer) {
var sharedStyles=doc.documentData ().layerStyles ();
sharedStyles.addSharedStyleWithName_firstInstance («Custom Style», layer. style ());
}

Создание общего стиля с нуля:
var doc = context. document;
var sharedStyles=doc.documentData ().layerStyles ();
var style=MSStyle.alloc ().init ();
var fill=style.fills ().addNewStylePart ();
fill.color = MSColor. colorWithSVGString («#B1C151»);
sharedStyles.addSharedStyleWithName_firstInstance («Custom Style 2», style);
doc.reloadInspector ();

Готовые примеры:

Работает в:

  • Sketch 3.1 +

Недостающий 'MSColor.colorWithHex:alpha:'? 🙂

До версии Sketch 3.2 был очень удобный метод класса под названием MSColor.colorWithHex:alpha:, который позволял создать сущность класса MSColor с 16-ричной строкой, но, к сожалению, с релизом версии Sketch 3.2 его убрали из API.

Хорошие новости! Существует достойная замена этого метода:

// Create color without alpha.
var color = MSColor. colorWithSVGString («#FF0000»);
print (color);
// -> (r:1.0 g:0.0 b:0.0 a:1.0)
// Create color with alpha.
var color = MSColor. colorWithSVGString («#FF0000»);
color.alpha = 0.2;
print (color);
// -> (r:1.0 g:0.0 b:0.0 a:0.200 000)

Работает в:

  • Sketch 3.0 +

Flatten Vector Layer

Если вы хотите выполнить сведение векторного слоя, который содержит несколько внутренних контуров, объединенных с помощью разных булевых операторов в одном слое, вы можете воспользоваться методом +MSShapeGroup.flatten.

Flatten Vector Layer

Этот пример кода уплощает первый выделенный векторный слой:

var selection = context. selection;
var layer=selection.firstObject ();
if (layer && layer. isKindOfClass (MSShapeGroup)) {
layer.flatten ();
}

Готовый пример:

Работает в:

  • Sketch 3.2 +

Свести слои в растр

Примечание: Этот пример на данный момент не работает в Sketch 3.3

Чтобы свести один или несколько слоев в единый MSBitmapLayer, используйте метод MSLayerFlattener.flattenLayers:. Он принимает аргументы в виде массива слоев, которые нужно свести.

Свести слои в растр

Следующий пример сводит все выделенные слои в растровый слой:

var flattener = MSLayerFlattener. alloc ().init ();
flattener.flattenLayers (selection);

Готовый пример:

Работает в:

  • Sketch 3.2 +

Конвертировать текстовый слой в контуры

Чтобы конвертировать существующий MSTextLayer в слой MSShapeGroup, вам нужно получить представление текста NSBezierPath и конвертировать его в слой MSShapeGroup.

Конвертировать текстовый слой в контуры

Этот код демонстрирует, как получить векторный контур текстовых слоев и использовать его для создания векторной фигуры:

function convertToOutlines (layer) {

if (!layer.isKindOfClass (MSTextLayer)) return;

var parent=layer.parentGroup ();
var shape=MSShapeGroup.shapeWithBezierPath (layer.bezierPathWithTransforms ());

shape.style = layer. style ();
var style=shape.style ();
if (!style.fill ()) {

var fill=style.fills ().addNewStylePart ();
fill.color = MSColor. colorWithNSColor (layer.style ().textStyle ().attributes ().NSColor);

}

var isSelected=layer.isSelected ();
shape.name = layer.name ();
shape.setIsSelected (isSelected);

parent.removeLayer (layer);
parent.addLayers ([shape]);

return shape;

}

var selection = context. selection;
var layer=selection.firstObject ();
if (layer) {

var vectorizedTextLayer=convertToOutlines (layer);
print (vectorizedTextLayer);

}

Готовый пример:

Работает в:

  • Sketch 3.1 +

Получение координат точек на кривой

Если вы хотите распределить некоторые фигуры по кривой, есть очень удобный способ —
pointOnPathAtLength:, который реализован в расширении класса NSBezierPath_Slopes.

Этот метод принимает значение double, которое представляет позицию на кривой, в которой вы хотите получить координаты точки. Возвращает CGPoint с координатами точки.

Получение координат точек на кривой

Следующий пример делит кривую на 15 сегментов и выводит координаты граничных точек сегментов:

var selection = context. selection;
var layer=selection.firstObject ();
if (layer && layer. isKindOfClass (MSShapeGroup)) {

var count=15;
var path=layer.bezierPathWithTransforms ();

var step=path.length ()/count;
for (var i=0;i<=count;i++) {

var point=path.pointOnPathAtLength (step*i);
print (point);

}

}

Готовые примеры:

Работает в:

  • Sketch 3.2 +

Скачать плагин Sketch Plugins Cookbook