Керівництво новачка по розробці плагінів для графічного редактора Sketch



Вітаю друзі! Мене звуть Антон, я розвиваю сайт sketchapp.me присвячений графічному редактору Sketch. Дуже часто мені на пошту приходять питання про тонкощі розробки плагінів для Sketch. Я не розробник і не фахівець у створенні плагінів, тому я вирішив зробити переклад самого докладного керівництва щодо створення плагінів від Mike Mariano.

Частина 1 — З чого почати?
Ви хочете почати писати Sketch-плагіни і не знаєте, з чого почати? Продовжуйте читати, так як цей пост як раз для вас!

Освоїти базу не так просто. Є маса прикладів вже існуючих плагінів, але дуже складно зрозуміти, з чого потрібно почати. В допомогу вам я зібрав усю потрібну інформацію, яку вдалося відшукати, в одному місці.

Звичайно, це не мануал з написання плагінів для просунутих, так як я сам розробник. Я UI/UX-дизайнер, яким іноді доводиться кодити на досить непоганому рівні (принаймні, я так думаю). Тру-програмісти відверто плачуть, бачачи мій код, але я думаю, що якраз такий код добре зрозумілий новачкам.

Якщо ви інженер, який шукає більш складні приклади, вам буде корисний цей пост http://james.ooo/sketch-plugin-development, а також офіційний сайт Sketch-розробників: http://developer.sketchapp.com/

Навіщо писати плагін?
Плагіни ідеальні для автоматизації повторюваних завдань, вони значно спрощують роботу і продакшн. Є всі шанси, що вже існує плагін під ваші потреби, так що спочатку пошукайте готове рішення перед написанням свого. Дуже ймовірно, що хтось вже впорався із завданням краще за вас. Але якщо у вас унікальний робочий процес створення UI (як у мене з дизайном ігрових інтерфейсів в Unity), швидше за все вам буде потрібно кастомное рішення.

Початок
Перед тим, як ви приступите до коду, встановіть потрібні програми і закладки.
  1. Виберіть текстовий редактор, якщо у вас його ще немає. (Я використовую Atom, але є багато інших відмінних редакторів, таких як Sublime або Textmate).
  2. Відкрийте Консоль для дебага, і додайте її в свою панель Dock, ви часто будете нею користуватися.>1-8tsgn6bxwkezgshwyip8lw
  3. Консоль використовується вашою машиною для ВСЬОГО дебага, так що створіть новий фільтр журналу запитів Sketch: File > New System Query Log
Скопіюйте ці параметри і натисніть Ok.

1-n8clsi0_jhe-4jsfx-txga

Фільтр Sketch появиться в колонці зліва.

1-p0oatbbn78yfdssskqrigg

4. Зробіть закладку папки Sketch Plugins для швидкого доступу, додавши її до Обраного (Favorites) у вікні Finder.

До цієї папки ви теж будете часто звертатися, так що бажано мати її під рукою.

/Library/Application support/com.bohemiancoding.sketch3/Plugins

1-xermiwsh60b0osekwabugw

Ось і все, ви готові до написання свого першого плагіна!

Створення плагіна за 10 простих кроків
Плагіни Sketch — це папки з розширенням .sketchplugin, якими легко обмінюватися з іншими користувачами.

У цьому прикладі ми створимо базовий скрипт для отримання назви сторінки. Вам не потрібно знати програмування, щоб реалізувати це завдання, але вона допоможе зрозуміти базові принципи. У наступних постах я буду документувати різні скрипти для одержання різних даних з Sketch та їх зміни. Цей приклад найпростіший.

Плагіни Sketch пишуться на CocoaScript, який являє собою суміш Objective-C/Cocoa і JavaScript. Я непогано знайомий з Javascript, так що тут складнощів не виникло. Не скажу, що я в CocoaScript, як риба у воді, але моїх знань з JavaScript було достатньо, щоб розібратися.

Отже, почнемо!

1. Створіть нову папку в каталозі Sketch Plugins і назвіть її MyPlugin.sketchplugin

1-dw5qn1na_zd8dgzm6s141w
(як тільки ви додасте розширення .sketchplugin, подвійний клік на ній запустить встановлення плагіну замість відкриття папки. Щоб відкрити саму папку, клацніть правою кнопкою мишки на плагіні і виберіть опцію Show Package Contents).

2. Всередині папки створіть ще одну папку і назвіть її Contents

3. Всередині Contents створіть папку Sketch

Кінцева структура папок плагіна буде виглядати так:

1-mnhkmqkpjyg0taofttwtgw

Всередині папки Sketch ви і будете створювати сам плагін, який складається мінімум з 2 файлів – маніфесту й скрипта.

1-8l57idwssf5tnot0x408tq

Маніфест описує плагін і може містити різні гарячі клавіші і додаткові скрипти, він завжди називається manifest.json.

В скрипті міститься сам код плагіна, і на нього посилається маніфест. Ім'я можна змінювати на свій розсуд, але воно має збігатися в обох файлах.

4. В текстовому редакторі створіть новий файл під назвою manifest.json і збережіть його в MyPlugin.sketchplugin > Contents > Sketch

5. Скопіюйте та вставте цей код manifest.json, і збережіть.

{
"name" : "My Plugin",
"identifier" : "my.plugin",
"version" : "1.0",
"description" : "My First Sketch Plugin",
"authorEmail" : "your@email.com",
"author" : "Your Name",

"commands" : [
{
"script" : "MyScript.js",
"handler" : "onRun",
"shortcut" : "command shift y",
"name" : "Get Page Names",
"identifier" : "my.plugin.pagenames"
}
]
}


Тепер MyPlugin з'явиться у вашому меню плагінів Sketch. Ви можете змінити назву плагіна або гарячу клавішу його виклику, і це відіб'ється в меню Plugins у Sketch. Важливо вибрати таку гарячу клавішу запуску, яка не призначена для інших програм або плагінів, інакше вона не буде працювати.

Тепер створимо MyScript.js, на який посилається manifest. Переконайтеся, що назва файлу співпадає з назвою у файлі manifest!

6. Поверніться в текстовий редактор і створіть новий файл під назвою MyScript.js і також, збережіть його в папку MyPlugin.sketchplugin > Contents > Sketch folder

7. Скопіюйте та вставте цей код MyScript.js

var onRun = function(context) {

//reference the Sketch Document
var doc = context.document;

//reference all the pages in the document in an array
var pages = [doc pages];

//loop through the pages of the document
for (var i = 0; i < pages.count(); i++){
//reference each page
var page = pages[i];

//get the name of the page
var pageName = [page name];

//show the page name in the console
log(pageName);
}

}


Я поясню детальніше цей код у наступних частинах. А поки що спирайтеся на коментарі в рядках.

8. Перейдіть в Sketch і відкрийте новий файл

9. У меню Plugins виберіть MyPlugin > Get Page Names

1-uelcrvjw2vyxzgjdib58ya

10. Відкрийте консоль і внизу лода ви повинні побачити назву сторінки
10:54:42 PM Get Page Names (Sketch Plugin): Page 1

Спробуйте змінити назву сторінки в Sketch-файлі і перезавантажте плагін. Лог повинен показувати нову назву. Додайте ще одну сторінку і перейменуйте її, а потім запустити плагін, консоль тепер покаже назви обох сторінок.

Ось і все!
Я працюю в Sketch всього кілька тижнів, і вже вражений його потужністю і можливостями кастомізації. Ви можете завантажити поточну версію плагіна тут.

Частина 2 — Настроювані повідомлення

Я відкрив для себе чудовий текстовий редактор Atom, на який і переключився. Не знаю, чому досі не користувався ним, але тепер я попрощався зі своїм старим-добрим TextMate!

У попередній главі ми працювали над плагіном, який показував назви всіх сторінок вашого документа в Консолі. Тепер вивчимо інші способи відображення інформації. Вони потрібні, якщо ви хочете повідомляти користувачів (і себе) про різні речі, наприклад, про те, що плагін відпрацював, або що користувачу необхідно ввести якісь дані. Також вам не доведеться постійно перемикатися в консоль, щоб протестувати свої скрипти.

Є два додаткових способу сповіщення користувачів всередині Sketch:
  1. Повідомлення (Message) — короткий ненав'язливе повідомлення, яке відображається внизу додатки, ховається через короткий проміжок часу
  2. Вікно оповіщення (Alert Box) — стандартне спливаюче вікно, яке або запитує від користувача введення даних, або вимагає якусь дію для продовження.
Для кожного способу є свої випадки застосування, і зазвичай вам не доведеться перевантажувати плагін (користувачів) безліччю повідомлень. Поекспериментуйте з обома, щоб зрозуміти, який метод краще для вашого власного плагіна.

Повернемося до скрипта плагіна з першого прикладу і переробимо його.

var onRun = function(context) {

//reference the Sketch Document
var doc = context.document;

//reference all the pages in the document in an array
var pages = [doc pages];

//loop through the pages of the document
for (var i = 0; i < pages.count(); i++){
//reference each page
var page = pages[i];

//get the name of the page
var pageName = [page name];

//show the page name in the console
log(pageName);
}

}


Якщо розглядати цей фрагмент коду, pageName посилається на назву кожної сторінки в масиві сторінок документа. Це контейнер з даними, що містить всю інформацію про сторінку. Зазвичай для доступу до цих масивів використовуються цикл for, які итерируют через кожен об'єкт, щоб отримати або призначити певні значення.

У цьому рядку мінлива pageName відправляється в Консоль.

log(pageName);

Тепер додамо повідомлення вниз скрипта, який буде відображати повідомлення всередині Sketch, коли скрипт відпрацює.

Для цього ми додамо рядок коду після дужки циклу for:
doc.showMessage("MyPlugin Finished!");

Doc – це змінна вгорі скрипта, яка посилається на документ, а showMessage – це вбудована функція, в якій ми можемо передавати змінну або рядок (String) для відображення повідомлення.

Ось як виглядає її додавання після циклу for в контексті всього скрипта:

var onRun = function(context) {

//reference the Sketch Document
var doc = context.document;

//reference all the pages in the document in an array
var pages = [doc pages];

//loop through the pages of the document
for (var i = 0; i < pages.count(); i++){
//reference each page
var page = pages[i];

//get the name of the page
var pageName = [page name];

//show the page name in the console
log(pageName);
}
//show a message at the bottom of Sketch
doc.showMessage("MyPlugin Finished!");
}


Запустіть MyPlugin через меню Plugins, щоб побачити результат. Внизу вікна Sketch ви повинні побачити:

1-z9cmbfc9syuokwrzdsairq

Цей тип повідомлень корисний, якщо вам не хочеться постійно перемикатися в консоль, або якщо є щось, що ви хотіли б показати користувачеві, але що не вимагає його участі.

Якщо ви хочете більше виділяється повідомлення, яке вимагає від користувача якоїсь дії, краще використовувати alert box. Щоб додати його, допишіть рядок коду в початок скрипта, і ще одну після відображення попередніх повідомлень.

Перший рядок додається вгору, над оголошенням змінної doc, щоб звернутися до самого додатка:
var app = [NSApplication sharedApplication];

Друга рядок дає доступ до нової змінної для відправки повідомлень в додаток.
[app displayDialog:"This is an alert box!" withTitle:"Alert Box Title"];

І ось як виглядає кінцевий код з новими рядками:

var onRun = function(context) {

//reference the Application
var app = [NSApplication sharedApplication];

//reference the Sketch Document
var doc = context.document;

//reference all the pages in the document in an array
var pages = [doc pages];

//loop through the pages of the document
for (var i = 0; i < pages.count(); i++){
//reference each page
var page = pages[i];

//get the name of the page
var pageName = [page name];

//show the page name in the console
log(pageName);
}
//show a message at the bottom of Sketch
doc.showMessage("MyPlugin Finished!");

//send an alert message to the application
[app displayDialog:"This is an alert box!" withTitle:"Alert Box Title"];
}


Запустіть MyPlugin з меню Plugin, і ви повинні побачити щось таке:

1-1aqb4gdfoh7dxzdugdqdzq

Міняйте повідомлення (ті, що в лапках) на що завгодно і перезавантажте плагін. Повна кастомізація!

Це самий початок налагодження і відображення даних всередині Sketch. Ви можете спробувати змінити скрипти повідомлень/повідомлень, додати додаткові скрипти для показу різних даних — змінних або лічильників масивів.

Щоб показати кількість сторінок у документі, ви можете створити повідомлення з відображенням pages.count().
[app displayDialog:"This document has" + pages.count() + " pages." withTitle:"Alert Box Title"];

Ось таким буде результат:

1-ruhpur9ob2ns-kknochjyq

Ми розібралися з одними повідомленнями! Спочатку підготували середовище для розробки, потім розібралися, як дебажити і відображати повідомлення всередині Sketch. Далі ми сфокусуємося більше на самому коді, пошуку конкретних даних і способи їх обробки.

Поточну версію плагіна можна скачати тут.

Частина 3 — Написання коду для багаторазового використання
Настав час розгорнути монітори вертикально, тому що ми приступаємо до програмування!

У цій частині ми розкриємо базові концепти написання коду, створення функцій для повторного виклику, а також линкование двох скриптів разом. Якщо ви вже освоїлися в написанні скриптів, можете просто проскроллить вниз до кінця цієї частини, завантажити вихідні коди і переробити їх на свій лад. Якщо ж ви новачок в цій справі, або вам потрібно освіжити знання, продовжуйте читати!

Базові концепти написання коду
Є код, який ми вже написали, і він включає в себе змінні, масиви, функції та цикли for. Ці елементи, а також оператори if/else, і є фундаментальні цеглинки все, що я роблю. Як тільки я освоїв ці елементи (особливо, цикл loop), мені стало набагато простіше розуміти скрипти. Я пишу свої скрипти на Javascript і Objective-C, але ці концепти досить універсальні для будь-якої мови програмування. Вам просто потрібно виробити певний спосіб написання коду.

Я нашвидку поясню ці поняття:

Мінлива — це посилання на якийсь тип інформації. Це може бути як рядок шматок тексту), так і число або булевское значення (true або false). Це гаряча клавіша доступу до значення, і дуже зручно використовувати саме її замість того, щоб друкувати його руками знову і знову.
var myName = "Mike";
var thisYear = 2016;
var givesHighFives = true;

Якщо я поставлю для myName значення «Mike», тоді я просто можу писати скрізь в скрипті myName, і ця змінна буде посилатися на «Mike». Але якщо я захочу, щоб значенням myName став «Шон», мені потрібно буде змінити його тільки один раз в оголошенні змінної, і кожна її сутність в коді також зміниться.

Масив — це теж мінлива, але зазвичай він містить кілька однотипних значень. Як якщо б у вас був масив днів тижня, ви б побачили стринговое значення всіх днів всередині такого масиву.
var daysOfTheWeek = {"Sunday", "Monday", "Tuesday", Wednesday", "Четвер", "П'ятниця", "Субота"};

Ви також можете написати його так, буде простіше побачити кількість елементів масиву, які починаються з 0:
var daysOfTheWeek[];
daysOfTheWeek[0] = "Sunday";
daysOfTheWeek[1] = "Monday";
daysOfTheWeek[2] = "Tuesday";
daysOfTheWeek[3] = "Wednesday";
daysOfTheWeek[4] = "Thursday";
daysOfTheWeek[5] = "Friday";
daysOfTheWeek[6] = "Субота";

Функція — це багаторазово використовується фрагмент коду, який виконує певне завдання. Зазвичай ви їх ставите, якщо потрібно робити щось знову і знову. Ви можете передавати в неї змінні, змушувати їх повертати значення або тримати їх порожніми.
var eyesOpen = true;
function closeEyes(){
eyesOpen = false;
}
function openEyes(){
eyesOpen = true;
}

У прикладі вище, кожен раз, коли ви хочете закрити очі, ви можете викликати функцію closeEyes(), потім для їх відкриття викликати openEyes().

Оператор if/else робить саме те, що говорить, тобто перевіряє якесь умова, виробляє певну дію, якщо умова виконується, в іншому випадку виконує щось інше.
if(daysOfTheWeek == "Sunday"){
watchGameOfThrones();
}else if(daysOfTheWeek == "Wednesday"){
watchMrRobot();
}else{
watchTheNews();
}

Цикл for використовується для повтору певного блоку коду відоме кількість разів. Наприклад, у масиві daysOfTheWeek, якщо б ви хотіли вивести значення масиву в список, ви б скористалися для цього цикл for.

Без циклу ви б писали так:
log(daysOfTheWeek[0]);
log(daysOfTheWeek[1]);
log(daysOfTheWeek[2]);
log(daysOfTheWeek[3]);
log(daysOfTheWeek[4]);
log(daysOfTheWeek[5]);
log(daysOfTheWeek[6]);

А з циклом вам би довелося написати лише:
for(var i = 0; i < daysOfTheWeek.count(); i++){
log(daysOfTheWeek[i];
}

Набагато простіше, правда? Для циклу ми поставили початкове значення i, рівне 0 і вважали, i поки не досягне кількості значень у масиві daysOfTheWeek, тобто 7. Тобто починаючи з i=0, цикл логирует значення daysOfTheWeek, потім додає до i 1, і все повторюється. Все це проробляється 7 разів, поки не досягається повний набір значень у масиві.

Створення функції вікна сповіщення (Alert Box)
Повернемося до кодуMyPlugin. У попередній частині ми створили вікно оповіщення, яке відображалося після відпрацювання сценарію.
[app displayDialog:"This is an alert box!" withTitle:"Alert Box Title"];

Виглядає як щось, що буде використовуватись кожен раз, коли нам знадобиться показати повідомлення користувачеві, і не особливо хочеться кожен раз прописувати весь рядок коду для кожного такого повідомлення. Це особливо актуально, якщо коротке повідомлення — це комбінація змінних і рядків, код може бути досить довгий.

Відмінний привід зробити повторно використовувану функцію, та для цього потрібно прописати:

function alert(title, message){
var app = [NSApplication sharedApplication];
[app displayDialog:message withTitle:title];
}


Кожен раз, коли ми викликаємо alert() і передаємо заголовок і повідомлення буде відображатися вікно оповіщення.

Додайте цю функцію в кінець скрипта, після дужок функції onRun:

var onRun = function(context) {

//reference the Application
var app = [NSApplication sharedApplication];

//reference the Sketch Document
var doc = context.document;

//reference all the pages in the document in an array
var pages = [doc pages];
[app displayDialog:"This document has " + pages.count() + " pages." withTitle:"Alert Box Title"];

//loop through the pages of the document
for (var i = 0; i < pages.count(); i++){

//reference each page
var page = pages[i];

//get the name of the page
var pageName = [page name];

//show the page name in the console
log(pageName);

}

//show a message in app
doc.showMessage("MyPlugin Finished!");

//send an alert message to the application
[app displayDialog:"This is an alert box!" withTitle:"Alert Box Title"];

}

function alert(title, message){
var app = [NSApplication sharedApplication];
[app displayDialog:message withTitle:title];
}


І тепер ми можемо змінити код, написаний раніше, замінивши їм вихідну посилання на змінну, так як тепер вона прописана у функції. Скрипт тепер виглядає так:

var onRun = function(context) {

//reference the Application
var app = [NSApplication sharedApplication];

//reference the Sketch Document
var doc = context.document;

//reference all the pages in the document in an array
var pages = [doc pages];
alert("Number of Pages", "This document has " + pages.count() + " pages.");

//loop through the pages of the document
for (var i = 0; i < pages.count(); i++){

//reference each page
var page = pages[i];

//get the name of the page
var pageName = [page name];

//show the page name in the console
log(pageName);

}

//show a message in app
doc.showMessage("MyPlugin Finished!");

//send an alert message to the application
alert("Plugin Finished!", "This is a message saying the Plugin is finished.")

}

function alert(title, message){
var app = [NSApplication sharedApplication];
[app displayDialog:message withTitle:title];
}


Запустіть плагін, і ви будете бачити такий же результат, як раніше, але тепер ваш код посилається на функцію. Її можна викликати в будь-якому місці скрипта, і не треба буде переписувати код знову і знову. Але що якщо б у нас був інший скрипт, в якому потрібно використовувати цю функцію?

Посилання на скрипти з інших скриптів
І ось тут доречно створити бібліотеку. Іноді у вас є загальна функція, яку не варто прив'язувати до якогось одного скрипту. Як у випадку з функцією alert(), або з функцією округлення числа до найближчого цілого. Це загальні функції, які, швидше за все, будуть використовуватися в різних скриптах. Тому замість копіпаста функції в кожен файл ви можете створити загальний файл, і вставляти його за необхідності.

1. Збережіть новий JavaScript-файл в ту ж папку, в якій зберігається MyScript.js і назвіть його common.js. Тепер в папці буде 3 файлу:

1-c9uz7wbojajdje_xvd0ziw

2. Виріжте функцію alert з MyScript.js та вставте її в common.js і збережіть зміни.

В common.js повинен бути тільки цей код:

function alert(title, message){
var app = [NSApplication sharedApplication];
[app displayDialog:message withTitle:title];
}


3. Щоб запустити цей скрипт з MyScript.js додайте цей рядок коду вгорі скрипта і збережіть:
@import 'common.js'

Весь скрипт з новим рядком зверху та віддаленої функцією alert() виглядає так:

@import 'common.js'

var onRun = function(context) {

//reference the Application
var app = [NSApplication sharedApplication];

//reference the Sketch Document
var doc = context.document;

//reference all the pages in the document in an array
var pages = [doc pages];
alert("Number of Pages", "This document has " + pages.count() + " pages.");

//loop through the pages of the document
for (var i = 0; i < pages.count(); i++){

//reference each page
var page = pages[i];

//get the name of the page
var pageName = [page name];

//show the page name in the console
log(pageName);

}

//show a message in app
doc.showMessage("MyPlugin Finished!");

//send an alert message to the application
alert("Plugin Finished!", "This is a message saying the Plugin is finished.")

}


Запустіть плагін ще раз, він повинен працювати, як і раніше, але тепер ви запускаєте функцію загального скрипта! Оптимізація завершено!

Тепер у вас є основи написання і розуміння більш складних плагінів. Далі ми вивчимо доступ до даних документа Sketch через його змінні та масиви, способи проходження по цих масивах для вилучення і призначення потрібних нам значень.

Ви можете завантажити поточну версію плагіна тут.

Частина 4 — Приклади з реального світу
Ми вже навчилися, як організовувати середовище для написання і налагодження коду, як відображати різні види користувальницьких повідомлень, а також освоїли базові поняття написання коду. Здається, у нас є все, щоб почати писати власні плагіни.

На щастя, Bohemian Coding мають документацію за своїм класам:
http://developer.sketchapp.com/reference/class/

На жаль, як пишеться спочатку цієї сторінки, вона все ще в стадії розробки". Так що не все ще описано, і не так багато прикладів, що з цим всім робити.

Особисто я краще всього вчуся на прикладах. Розуміння коду може викликати великі труднощі, але коли ви бачите, як він використовується стосовно до ваших знань і цілям, стає набагато простіше. Я наведу три приклади, які знайшов, поки працював з Sketch, сподіваюся, вам вони виявляться корисними!

Приклад 1: Перемикання висоти і ширини артборда.
Дивно, але в стандартному функціоналі Sketch немає зміни орієнтації артборда з портретної на альбомну і навпаки. Я пошукав, і не знайшов жодного плагіна, який вирішує цю проблему, так що саме час створити наш власний!

Для ясності давайте визначимося, що ми будемо робити. Це допомагає визначити мету, способи її досягти, а також виявити крайні випадки, з якими доведеться зіткнутися. Це майже як створення UX-сценарію, правда?

Для цього плагіна ми напишемо скрипт, який виконує наступне:
  • Спочатку переконується, що щось виділено
  • Потім переконується, що виділений об'єкт — це артборд
  • Потім бере початкову висоту і ширину обраного артборда і зберігає їх у змінні
  • Потім задає нову висоту на основі його старої ширини, і нову ширину на основі старої висоти
  • В кінцевому підсумку, повідомляє користувача, що скрипт відпрацював


Додавання ще одного скрипта в MyPlugin

Замість додавання нової порції коду MyScript.js давайте створимо новий скрипт під назвою RotateArtboard.js, і збережіть його до папки MyPlugin.sketchplugin.

Додайте цей код у RotateArtboard.js. Кожен основний скрипт повинен бути у функції onRun, так що цей скрипт завжди є хорошою базою для старту:

@import 'common.js'
var onRun = function(context) {
//reference the Sketch Document
var doc = context.document;
}


Ви побачите, що ми імпортуємо файл common.js щоб використовувати ту ж функцію alert, яку ми вже створили.

На даний момент в папці плагіна повинні бути наступні файли:

1-zq4ryj_udiwcr-biqkupkg

Тепер відкрийте manifest.json, щоб додати ще один скрипт в наш маніфест.

Скопіюйте фрагмент коду з усіма командами MyScript.js додайте кому після блоку MyScript, і вставити перед закритою дужкою commands, ось так:

{
"name" : "My Plugin",
"identifier" : "my.plugin",
"version" : "1.0",
"description" : "My First Sketch Plugin",
"authorEmail" : "your@email.com",
"author" : "Your Name",

"commands" : [
{
"script" : "MyScript.js",
"handler" : "onRun",
"shortcut" : "command shift y",
"name" : "Get Page Names",
"identifier" : "my.plugin.pagenames"
},
{
"script" : "RotateArtboard.js",
"handler" : "onRun",
"shortcut" : "command shift u",
"name" : "Rotate Artboard",
"identifier" : "my.plugin.rotateartboard"
}
],
}


Тепер ви бачите новий скрипт в меню MyPlugin (можете змінювати гарячі клавіші, до речі):

1-hv9u76wafmq1rnjitphdg

Повернемося до коду!

Щоб дізнатися, що виділено, використовуємо наступне:
var selection = context.selection;

Цей код створює змінну типу масив з усіма виділеними шарами. Тепер ми можемо перевірити цей масив, щоб дізнатися, чи є що-небудь всередині нього. Якщо там 0, тоді нічого не виділено, і ми скажемо користувачеві, що потрібно щось виділити.
if(selection.count() == 0){
doc.showMessage("Please select something.");
}

Але якщо лічильник не 0, тоді всередині масиву щось є, як мінімум, один шар повинен бути виділений. Тепер ми можемо пройтися циклом по виділених шарів, щоб визначити, чи є серед них артборды (MSArtboardGroup):
if(selection.count() == 0){
doc.showMessage("Please select something.");
} else {
for(var i = 0; i < selection.count(); i++){
if(selection[i].class() == "MSArtboardGroup"){
//do something
}
}
}

Ви можете використовувати цю ж техніку для перевірки, чи є виділення текстовим шаром (MSTextLayer), групою (MSGroupLayer), фігурою (MSShapeGroup / не задокументована), імпортованим зображенням (MSBitmapLayer або символом (MSSymbolInstance / не задокументована).

Щоб отримати висоту і ширину артборда, спочатку потрібно отримати його кадр. І вже з кадру можна отримати значення, і призначити нові.

Створіть змінну під назвою artboard на основі елемента масиву в циклі for, і так ми зможемо отримати поточні розміри. Використання циклу for дозволяє працювати з декількома виділеними артбордами одночасно.
var artboard = selection[i];
var artboardFrame = artboard.frame();
var artboardWidth = artboardFrame.width();
var artboardHeight = artboardFrame.height();

Тепер створіть дві нові змінні із значеннями, які нам потрібні, тобто поміняйте місцями ширину і висоту:
var newArtboardWidth = artboardHeight;
var newArtboardHeight = artboardWidth;

Потім скористаємося artboardFrame, щоб задати висоту і ширину новими змінними:
artboardFrame.setWidth(newArtboardWidth);
artboardFrame.setHeight(newArtboardHeight);

І, нарешті, ми скористаємося нашим вікном повідомлень, щоб повідомити користувачеві про відпрацювання сценарію і відобразити нові значення:
var alertMessage = “New Height: “+newArtboardHeight+ “ | New Width: “+newArtboardWidth;
alert("Artboard Rotated!", alertMessage)

Ось весь скрипт з коментарями:

@import 'common.js'

var onRun = function(context) {

//reference the sketch document
var doc = context.document;
//reference what is selected
var selection = context.selection;

//make sure something is selected
if(selection.count() == 0){
doc.showMessage("Please select something.");
}else{
//loop through the selected layers
for(var i = 0; i < selection.count(); i++){

//checks to see if the layer is an artboard
if(selection[i].class() == "MSArtboardGroup"){

//reference the selection
var artboard = selection[i];

//get the artboard frame for dimensions
var artboardFrame = artboard.frame();
//get the width
var artboardWidth = artboardFrame.width();
//get the height
var artboardHeight = artboardFrame.height();

//set a new variable width to the old height
var newArtboardWidth = artboardHeight;
//set a new height variable to the old width
var newArtboardHeight = artboardWidth;

//set the artboard frame with the new dimensions
artboardFrame.setWidth(newArtboardWidth);
artboardFrame.setHeight(newArtboardHeight);

//send an alert message with the new values
var alertMessage = "New Height: "+newArtboardHeight+ " | New Width: "+newArtboardWidth;
alert("Artboard Rotated!", alertMessage);
}else{
doc.showMessage("Please select an artboard.");
}
}
}
}


Підсумуємо приклад 1:
  • Перевіряємо, виділено щось
  • Визначаємо тип виділеного шару, і робимо дещо-що, якщо це той тип, який нам потрібен
  • Витягаємо вихідний кадр нашого виділеного артборда, висоту і ширину, і зберігаємо це в змінних
  • Створюємо нові змінні c потрібними нам значеннями
  • Задаємо для виділеного кадру артборда ці значення


Приклад 2: Виділення шарів і їх перейменування на ім'я символу
Ось ще одна проблема з реального світу, яку я виявив, працюючи з символами. Якщо я додаю новий символ із своєї бібліотеки, ім'я шару змінюється на ім'я символу. Якщо я переключаюсь на інший символ, назва шару все одно залишається без змін. Іноді я хочу задати свою назву шару, а іноді просто хочу переключити його на назву нового символу. Ось ще один ідеальний привід написати плагін!

Для прикладу, скачайте файл Sketch для довідок.

У файлі Sketch на першому артборде ви побачите символ під назвою Square, він знаходиться по центру. Також в бібліотеці є ще один символ під назвою Circle.

Виділіть Square, і використовуючи опції праворуч змініть Square на Circle

1-ow6hbt63d3_y9d7hzdkd0a

Тепер подивіться на шари зліва — шар все ще називається Square! Я думаю, що міг би змінити назву вручну, але це не прикольно, особливо якщо потрібно змінити безліч сутностей.

Сценарій для цього плагіна такий:
  • Спочатку переконатися, що щось виділено
  • Потім переконатися, що виділено саме символ
  • Отримати назву символу
  • Перевірити, чи збігається назву символу з назвою шару
  • Якщо ні, змінити назву шару на назву символу
  • І, нарешті, повідомити користувача, коли скрипт відпрацює
Ви помітите, що у нового і попереднього скрипта багато спільного – перевірка на виділення і визначення типу виділених об'єктів. Так що ми можемо скористатися готовими напрацюваннями, але все ж це досить специфічний випадок, так що будемо створювати окремий скрипт.

Слідуючи першим крокам з попереднього прикладу, давайте додамо ще один скрипт в MyPlugin.sketchplugin, створивши новий файл під назвою SetLayerNameToSymbolName.js і додамо його в manifest.json. Щоб заощадити час, просто використовуйте RotateArtboard.js і «Save As». Ми будемо використовувати цей скрипт дуже часто, так що почнемо звідси і зробимо кілька змін.

аша папка зараз містить такі файли:

1-wswvgjzkj299_nctanol8g

У цьому скрипті, замість перевірки, чи є виділення MSArtboardGroup (артбордом), ми перевіримо, чи є воно MSSymbolInstance (символом). Якщо є, ми з'ясуємо назва symbolMaster і порівняємо його з назвою шару.

І ось вам новий скрипт з парою змінених рядків:

@import 'common.js'

var onRun = function(context) {

//reference the sketch document
var doc = context.document;
//reference what is selected
var selection = context.selection;

//make sure something is selected
if(selection.count() == 0){
doc.showMessage("Please select something.");
}else{
//loop through the selected layers
for(var i = 0; i < selection.count(); i++){

//checks to see if the layer is a Symbol
if(selection[i].class() == "MSSymbolInstance"){

//reference the selection
var layer = selection[i];
//get the original layer name
var layerName = layer.name();
//get the name of the symbol on the layer
var symbolName = layer.symbolMaster().name();

//check if layer name is not already symbol name
if(layerName != symbolName){
//set the layer name to the symbol name
layer.setName(symbolName);

var alertMessage = "Layer Name Changed from: "+layerName+ " to: "+symbolName;
alert("Layer Name Changed!", alertMessage);

}else{
doc.showMessage("Layer name is already Symbol Name.");
}

}else{
doc.showMessage("Please select a Symbol Layer.");
}
}
}
}


Підсумуємо приклад 2

У цьому прикладі ми засвоїли:
  • Перевірки, чи є виділення артбордом або символом, виконуються ідентично.
  • Як отримати назву symbolMaster і назва шару
  • Як порівняти два назви та налаштувати назва шару, якщо вони відрізняються.


Приклад 3: Завдання назв символів в якості назв шарів
Попередній скрипт працює ідеально, але інколи мені не хочеться виділяти кожен шар вручну. Іноді після розкидання купи символів по документу я просто хочу привести всі назви шарів у відповідність з назвами символів без виділення шару. Для цього знадобиться змінити наступне:
  • Спочатку отримати всі сторінки документа
  • Потім отримати всі артборды на кожній сторінці
  • Потім отримати всі шари на кожному артборде
  • Потім перевірити, чи є якийсь із цих шарів шаром символу
  • Потім перевірити, не збігається назва шару з назвою символу
  • Якщо не збігається, замінити назву шару і повідомити користувача
Так як ми не враховуємо, що зараз виділено, нам доведеться змінити скрипт, щоб він пройшовся з даними сторінок документа. Потім ми скористаємося рядом вкладених петель for, які будуть итерировать по масиву артбордов, потім по масиву шарів, і нарешті, дійдуть до кожного шару. Цикл for всередині циклу for, яка всередині циклу for.

Напевно, для цієї задачі ми створимо ще один новий скрипт. Почніть з нового файлу під назвою SetAllLayerNamesToSymbolNames.js і також додайте його в manifest.json, як ми робили раніше.

Тепер в папці MyPlugin.sketchplugin буде стільки файлів:

1-rhsu-brcrkmcsyvbno0ckw

І плагін у вашому списку меню, разом з попередніми прикладами повинен відображати нову опцію, як тільки ви додасте новий файл в маніфест:

1-zl8p9tmk09ocpqo0ujd7gw

Щоб витягти дані сторінок документа, використовуйте наступний код:
var pages = [doc pages];

Щоб отримати артборды сторінок, використовуйте цикл for:

var pages = [doc pages];

for (var i = 0; i < pages.count(); i++){
var page = pages[i];
var artboards = [page artboards];
}


Щоб отримати шари артборда, використовуйте цикл for всередині циклу for:

var pages = [doc pages];

for (var i = 0; i < pages.count(); i++){
var page = pages[i];
var artboards = [page artboards];

for (var z = 0; z < artboards.count(); z++){
var artboard = artboards[z];
var layers = [artboard layers];

}
} 


Далі, щоб отримати кожен окремий шар, вам потрібно пройтися по верствам всередині циклу for, і потім перевірити, що це за шар:

var pages = [doc pages];

for (var i = 0; i < pages.count(); i++){
var page = pages[i];
var artboards = [page artboards];

for (var z = 0; z < artboards.count(); z++){
var artboard = artboards[z];
var layers = [artboard layers];

for(var k = 0; k < layers.count(); k++){
var layer = layers[k];

if(layer.class() == "MSSymbolInstance"){
//do something
}
}
}
}


Як тільки ми встановили, що це шар символу, ми можемо перевірити його назву і порівняти з назвою шару. Якщо вони відрізняються, змінюємо назву шару на назву символу, і все!

Ось як виглядає весь скрипт, для простоти розуміння майже кожна рядок прокоментована:

@import 'common.js'

var onRun = function(context) {
//reference the sketch document
var doc = context.document;
//reference the pages array in the document
var pages = [doc pages];
//create a variable to hold how many symbol layers we changed
var symbolCount = 0;

//loop through the pages array
for (var i = 0; i < pages.count(); i++){
//reference each page
var page = pages[i];
//reference the artboards array of each page
var artboards = [page artboards];

//loop through the artboards of each page
for (var z = 0; z < artboards.count(); z++){
//reference each artboard of each page
var artboard = artboards[z];
//reference the layers array of each artboard
var layers = [artboard layers];

//loop through the layers array
for(var k = 0; k < layers.count(); k++){
//reference each layer of each artboard
var layer = layers[k];

//check to see if the layer is a Symbol
if(layer.class() == "MSSymbolInstance"){

//get the original layer name
var layerName = layer.name();
//get the name of the symbol on the layer
var symbolName = layer.symbolMaster().name();

//only change the name of layers that don't match the symbol name
if(layerName != symbolName){
//set the layer name to the symbol name
layer.setName(symbolName);
symbolCount = symbolCount + 1;

}
}
}
}
}
var alertMessage = symbolCount + " symbol layer name changed.";
alert("Symbol Layer Names Reset!", alertMessage);
}


Підсумуємо приклад 3, тут ми навчилися:
  • Проходитися по всіх сторінок документа, щоб отримати список артбордов
  • Проходитися по всьому артбордам сторінки, щоб отримати шари
  • Проходитися по всім верствам артборда і перевіряти їх тип
  • Порівнювати ім'я шару з ім'ям символів
  • І, нарешті, якщо імена відрізняються, змінити назву шару на назву символу


Висновок
Це лише три приклади, як можна почати використовувати базові поняття доступних класів Sketch, вивчених раніше. Тут ще багато чого можна зробити, але ми поки тільки починаємо! Далі ми розглянемо ще більше способів доступу до додаткових класами всередині Sketch, ще більше прикладів з реального світу.

Ви можете завантажити поточну версію плагіна тут.

Частина 5 — Клас MSLayer
Ми вже ознайомилися з реальними робочими прикладами того, що можуть зробити класи в Sketch, так що можна заглибитися у їх вивчення далі. У цій частині ми сфокусуємося на класі MSLayer, а потім вивчимо класи, похідні цей.

Клас MSLayer — це базовий клас всіх шарів у Sketch, і всі інші типи шарів — це підкласи, які успадковують його. Все, що ви додаєте в ваш документ, є спочатку шаром — сторінка, артборд, група, фігура, текст, зображення і т. д. Все це — підкласи класу MSLayer, і вони можуть все, що може клас MSLayer, і навіть більше.

Щоб краще зрозуміти термінологію, є два типи даних, доступних через клас:
  1. Атрибути — іменоване властивість об'єкта (наприклад, висота, ширина або назва)
  2. Методи — функції, пов'язані з класами, зазвичай використовується для зміни атрибутів (таких як зміна висоти, ширини або назви)
Документація на сайті розробників вже містить непогану базу атрибутів і методів класів. Так що немає сенсу відтворювати всі тут. Замість цього я покажу вам найпростіші способи перегляду атрибутів шару і використання його методів у реальному світі.

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

Припустимо, що ви використовували масив виділення, щоб отримати змінну під назвою layer:

var onRun = function(context) {
var doc = context.document;
var selection = context.selection;

if(selection.count() == 0){
doc.showMessage("Please select something.");
}else{
for(var i = 0; i < selection.count(); i++){
var layer = selection[i];
}
}
};


Знаючи, що всі верстви — це підкласи базового класу MSLayer, ми можемо отримати деякі атрибути будь-якого типу шарів, незалежно від його специфіки, а потім зберігати їх у вигляді змінних для доступу в майбутньому.

Деякі атрибути є насправді сутностями інших класів, і мають свої власні атрибути і методи на базі цих класів. Для них я покажу декілька прикладів, як ви можете заглибитися в дані. Але для більш ретельного вивчення питання використовуйте посилання на документацію.

Ось кілька атрибутів, які доступні в класі MSLayer:

Клас (тип шару):
var layerClass = layer.class();

Фрейм (розмір і положення, щодо його артборда, сутність MSRect):
var layerFrame = layer.frame();

var layerWidth = layerFrame.width();
var layerHeight = layerFrame.height();
var layerXpos = layerFrame.x();
var layerYpos = layerFrame.y();

Стиль кордону, заливки, тіні, внутрішні тіні, сутність MSStyle):
var layerStyle = layer.style();

//отримання масиву квітів заливки
var layerFills = layer.style().fills();

//отримання кожного кольору заливки
for(var z = 0; z < layerFills.count(); z++){
var fillColor = layerFills[z].colorGeneric();
}

Назва шару:
var layerName = layer.name();

Видимість:
var layerIsVisible = layer.isVisible();

Статус блокування:
var layerIsLocked = layer.isLocked();

Відображення (горизонтальне або вертикальне):
//горизонтальне
var layerIsFlippedHorizontal = layer.isFlippedHorizontal();

//вертикальне
var layerIsFlippedVertical = layer.isFlippedVertical();

Поворот:
var layerRotation = layer.rotation();

Батьківська група (сторінка, артборд або група):
var layerParent = layer.parentGroup();

Статус виділення:
var layerIsSelected = layer.isSelected();

Absolute Rect (глобальний розмір та положення в цілому документі, сутність MSAbsoluteRect):
var layerAbsoluteRect = layer.absoluteRect();

var layerAbsoluteWidth = layerAbsoluteRect.width();
var layerAbsoluteHeight = layerAbsoluteRect.height();
var layerAbsoluteXpos = layerAbsoluteRect.x();
var layerAbsoluteYpos = layerAbsoluteRect.y();

CSSAttributeString:
var layerCSSAttributeString = layer.CSSAttributeString();

CSSAttributes:
var layerCSSAttributes = layer.CSSAttributes();

В документації є ще кілька, але це ті, які я зазвичай використовую. Я вибрав на основі потреб скрипта. Але якщо ви дійсно хочете зберегти всі ці атрибути відразу в змінні, ви б могли зробити щось таке:

var onRun = function(context) {
var doc = context.document;
var selection = context.selection;

if(selection.count() == 0){
doc.showMessage("Please select something.");
}else{
for(var i = 0; i < selection.count(); i++){
var layer = selection[i];
var layerClass = layer.class();
var layerFrame = layer.frame();
var layerStyle = layer.style();
var layerName = layer.name();
var layerIsVisible = layer.isVisible();
var layerIsLocked = layer.isLocked();
var layerIsFlippedHorizontal = layer.isFlippedHorizontal();
var layerIsVertical = layer.isFlippedVertical();
var layerRotation = layer.rotation();
var layerParent = layer.parentGroup();
var layerIsSelected = layer.isSelected();
var layerAbsoluteRect = layer.absoluteRect();
var layerUserInfo = layer.userInfo();
var layerCSSAttributeString = layer.CSSAttributeString();
var layerCSSAttributes = layer.CSSAttributes();
}
}
};


Потім ви можете використовувати консоль для логування будь-яких змінних, яких хотіли б бачити:
log(“Layer Rotation: “ + layerRotation);


Використання методів
Тепер, коли ми навчилися отримувати атрибути шару, ми можемо використовувати методи для їх налаштування. У класі MSLayer є кілька доступних методів, які дозволяють змінювати конкретні атрибути будь-якого шару. Їх назви видають їх дії:

setName
var newLayerName = "New Layer Name";
layer.setName(newLayerName);

setIsVisible
//показати шар
layer.setIsVisible(true);
//сховати шар
layer.setIsVisible(false);
//перемкнути
layer.setIsVisible(!layer.isVisible())

setIsLocked
//заблокувати шар
layer.setIsLocked(true);
//розблокувати шар
layer.setIsLocked(false);
//перемкнути
layer.setIsLocked(!layer.isLocked());

setRotation
var newLayerRotation = 180;
layer.setRotation(newLayerRotation);

setIsFlippedHorizontal
//відобразити по горизонталі
layer.setIsFlippedHorizontal(true);
//скинути
layer.setIsFlippedHorizontal(false);
//перемкнути
layer.setIsFlippedHorizontal(!layer.isFlippedHorizontal());

setIsFlippedVertical
//відобразити по вертикалі
layer.setIsFlippedVertical(true);
//скинути
layer.setIsFlippedVertical(false);
//перемкнути
layer.setIsFlippedVertical(!layer.isFlippedVertical());

setIsSelected
//виділити
layer.setIsSelected(true);
//зняти виділення
layer.setIsSelected(false);
//перемкнути
layer.setIsSelected(!layer.isSelected());

дублювання
layer.duplicate();

Тепер ви можете створювати різні типи плагінів, просто витягуючи атрибут шару і перенастраивая його.

Наприклад, якщо ви хочете розблокувати всі шари, ви спочатку можете перевірити, заблоковані вони, потім можете розблокувати:

var onRun = function(context) {
var doc = context.document;
var selection = context.selection;

if(selection.count() == 0){
doc.showMessage("Please select something.");
}else{
for(var i = 0; i < selection.count(); i++){

var layer = selection[i];
var layerIsLocked = layer.isLocked();

if(layerIsLocked == true){
layer.setIsLocked(false);
}

}
}
};


Або якщо б ви хотіли додати префікс до назви шару:

var onRun = function(context) {
var doc = context.document;
var selection = context.selection;

if(selection.count() == 0){
doc.showMessage("Please select something.");
}else{
for(var i = 0; i < selection.count(); i++){

var layer = selection[i];
var layerName = layer.name();
var layerPrefix = "prefix_";

layer.setName(layerPrefix + layerName);

}
}
}


І ще багато всього, опції дійсно нескінченні!

Методи атрибутів
Атрибути, які є сутностями інших класів, таких як фрейм або стиль, також мають свої методи.

Наприклад, щоб змінити ширину шару до 100, вам знадобиться використовувати setWidth на змінною кадру:

var onRun = function(context) {
var doc = context.document;
var selection = context.selection;

if(selection.count() == 0){
doc.showMessage("Please select something.");
}else{
for(var i = 0; i < selection.count(); i++){

var layer = selection[i];
var layerFrame = layer.frame();
var newLayerWidth = 100;

layerFrame.setWidth(newLayerWidth);

}
}
}


Якщо ви хочете додати шар заливку, ви можете використовувати addStylePartOfType(0) змінної стилю:

var onRun = function(context) {
var doc = context.document;
var selection = context.selection;

if(selection.count() == 0){
doc.showMessage("Please select something.");
}else{
for(var i = 0; i < selection.count(); i++){

var layer = selection[i];
var layerStyle = layer.style();

layerStyle.addStylePartOfType(0);

}
}
}


Визначення типу підкласу шару Finding the Layer Subclass Type
Це все ви можете робити з будь-яким MSLayer. Але що якщо, наприклад, ви хочете вважати рядок тексту з текстового поля? Вам потрібно знати спочатку, чи є цей об'єкт текстовим полем, інакше код викине вам помилку про неіснування String.

Не всі підкласи описані в документації, але так як ми можемо з'ясувати клас шару з атрибута класу, ми можемо легко з цим впоратися. І щоб ще більше спростити собі завдання, спеціально для цього я написав плагін!

Скачайте і встановіть плагін Show Layer Type Plugin тут

Потім скачайте і відкрийте файл Sketch для тестування.

У файлі Sketch ви побачите артборд з кількома різними типами шарів — 2 лінії, символ, 2 текстових поля і прямокутник.

1-4_g6dwvalei6aasionoktw

Виділіть всі шари і запустіть плагін Show Layer Type. В консолі ви побачите такий висновок:
8/28/16 9:07:18.993 AM Show Layer Type (Sketch Plugin)[46515]: Background is a: MSShapeGroup
8/28/16 9:07:18.997 AM Show Layer Type (Sketch Plugin)[46515]: Line is a: MSShapeGroup
8/28/16 9:07:18.999 AM Show Layer Type (Sketch Plugin)[46515]: Author is a: MSTextLayer
8/28/16 9:07:18.999 AM Show Layer Type (Sketch Plugin)[46515]: Icon is a: MSBitmapLayer
8/28/16 9:07:19.000 AM Show Layer Type (Sketch Plugin)[46515]: Oval is a: MSSymbolInstance
8/28/16 9:07:19.001 AM Show Layer Type (Sketch Plugin)[46515]: Title is a: MSTextLayer
8/28/16 9:07:19.002 AM Show Layer Type (Sketch Plugin)[46515]: Line is a: MSShapeGroup

Якщо ви перейдете на сторінку Symbols і виберіть символ, знову запустіть плагін, то побачите наступне:
8/28/16 9:10:08.600 AM Show Layer Type (Sketch Plugin)[46515]: Oval is a: MSSymbolMaster

Якщо ви виділите артборд і запустіть плагін, висновок буде таким:
8/28/16 9:10:48.226 AM Show Layer Type (Sketch Plugin)[46515]: Artboard 1 is a: MSArtboardGroup

Якщо ви згрупуєте всі шари на артборде, потім запустіть плагін, побачите наступне:
8/28/16 9:11:24.234 AM Show Layer Type (Sketch Plugin)[46515]: Group is a: MSLayerGroup

Ось і всі типи підкласів шарів! Можливо, є і інші, але це все, що мені вдалося знайти (документовані я супроводив посиланнями).

MSShapeGroup
MSBitmapLayer
MSTextLayer
MSSymbolInstance
MSSymbolMaster
MSArtboardGroup
MSLayerGroup

Тепер ми можемо диференціювати код в залежності від того, який це клас, використовуючи оператори if/else

var onRun = function(context) {
var doc = context.document;
var selection = context.selection;

if(selection.count() == 0){
doc.showMessage("Please select something.");
}else{
for(var i = 0; i < selection.count(); i++){
var layer = selection[i];
var layerClass = layer.class();

if(layerClass == "MSShapeGroup"){
//do something
} else if (layerClass == "MSBitmapLayer"){
//do something
} else if (layerClass == "MSTextLayer"){
//do something
} else if (layerClass == "MSSymbolInstance"){
//do something
} else if (layerClass == "MSSymbolMaster"){
//do something
} else if (layerClass == "MSArtboardGroup"){
//do something
} else if (layerClass == "MSLayerGroup"){
//do something
}
}
}
};


Кожен з цих підкласів володіє набагато більшою кількістю атрибутів і методів доступу, які ми розглянемо в інших постах. Вивчіть документацію з підкласами layer, щоб бути готовим до старту!

Висновки
У цій частині ми навчилися, як:
  • Переглядати атрибути шару
  • Змінювати атрибути шару з допомогою методів
  • Отримувати доступ до атрибутів атрибутів і методів
  • Перевіряти тип класу шару і розрізняти їх в коді


Частина 6 — Експорт даних
Sketch надає зручний доступ до вихідного коду svg і css через клік правою кнопкою миші на шарі. Це дуже корисна опція для веб-дизайнерів, але для тих, хто займається розробкою додатків або ігор користі мало. Також доводиться копіювати і вставляти значення з одного додатка в інше, що являє собою величезне поле для помилок.

Є багато сторонніх інструментів-містків між дизайном і розробкою (Zeplin, Avocode, Sketch Measure, Sympli та ін), але ці інструменти надають лише довідкове керівництво для розробників. Було б набагато корисніше, якщо програми для дизайну і розробки працювали разом напряму. Уявіть: ви робите дизайн чогось у Sketch, натискаєте «експортувати», прочитаєте свій проект по розробці і воссоздаете макет. Це позбавить від необхідності в попиксельных специфікаціях, і навіть заощадить час інженера на відтворенні дизайну в коді.

Набір інструментів, доступний для дизайнерів зараз, створювався як рішення для загальних потреб, вони не заточені під якісь специфічні завдання. Але в цьому і полягає принадність розробки плагінів: ви можете створювати інструменти чітко під свої потреби, потрібно тільки знати, як це робити.
Ми вже пройшлися по базі, отримали фундамент для успішного програмування. Тепер же ми можемо заглибитися в саме серце розробки плагінів, зробити реальне рішення для реальної задачі.

Плагіни Sketch — це інструменти, що сполучають дизайн з розробкою
Щоб з'єднати дизайн і розробку, треба знайти спільну основу цими двома сферами. Так як немає рідних способів їх з'єднати, що ми можемо використовувати таке, що зрозуміло обом? Відповідь — XML або JSON. За допомогою плагінів Sketch може експортувати дані в обидва формати, і, швидше за все, середовище розробки, в якій ви працюєте, може їх зчитувати. Так навіщо слати розробнику піксельні специфікації, з яких доведеться копіювати значення вручну, якщо можна відіслати їх у файлі XML, JSON і імпортувати автоматично?

Це дійсно одне з кращих рішень, можливих для Sketch. У цій частині я покажу базовий приклад, як експортувати атрибути шару в XML або JSON. За аналогією з попередніми частинами, давайте набросаєм план. Наш плагін буде працювати в такому порядку:
  1. Виділяємо шар
  2. Створюємо модальне вікно для вибору папки користувачем та збереження цього місця змінної.
  3. виділеного поля створюються змінні для збереження базової інформації (Назва, координата X, координата Y, висота і ширина).
  4. Зберігаємо всі ці змінні або в XML або JSON-об'єкт.
  5. І, нарешті, зберігаємо цей об'єкт у файл XML або JSON в попередньо обрану папку.


Експорт XML
Для початку ми почнемо зі створення нашого плагіна. Створіть плагін під назвою Export Layer to XML з основним скриптом під назвою ExportLayerToXML.js. Також потрібно додати наш файл common.js для використання кастомного вікна повідомлень, який створювали в попередніх частинах.

Ми вже знаємо, як визначити, виділено шар:

@import 'common.js'

var onRun = function(context) {
var doc = context.document;
var selection = context.selection;

//make sure something is selected
if(selection.count() == 0){
doc.showMessage("Please select a layer.");
}else{
//do something
}
};


Else В операторі ми створимо панель, де користувач буде вибирати, в яку папку зберегти файл:

//allow xml to be written to the folder
var fileTypes = [NSArray arrayWithObjects:@"xml", nil];

//create select folder window
var panel = [NSOpenPanel openPanel];
[panel setCanChooseDirectories:true];
[panel setCanCreateDirectories:true];
[panel setAllowedFileTypes:fileTypes];


Потім створимо змінну, яка буде перевіряти, чи натиснута кнопка, підключимо її до кнопці ОК, потім отформатируем обрану категорію і збережемо її в змінну для подальшого використання:

//create variable to check if clicked
var clicked = [panel runModal];
//check if clicked
if (clicked == NSFileHandlingPanelOKButton) {

var isDirectory = true;
//get the folder path
var firstURL = [[panel URLs] objectAtIndex:0];
//format it to a string
var file_path = [NSString stringWithFormat:@"%@", firstURL];

//remove the file:// path from string
if (0 === file_path.indexOf("file://")) {
file_path = file_path.substring(7);
}
}


Коли все буде зроблено, пройдемося циклом по виділених верств і викличемо функцію, яка записує дані в XML-файл. Вихідний код буде виглядати так:

@import 'common.js'

var onRun = function(context) {
var doc = context.document;
var selection = context.selection;

//make sure something is selected
if(selection.count() == 0){
doc.showMessage("Please select a layer.");
}else{
//allow xml to be written to the folder
var fileTypes = [NSArray arrayWithObjects:@"xml", nil];

//create select folder window
var panel = [NSOpenPanel openPanel];
[panel setCanChooseDirectories:true];
[panel setCanCreateDirectories:true];
[panel setAllowedFileTypes:fileTypes];

//create variable to check if clicked
var clicked = [panel runModal];
//check if clicked
if (clicked == NSFileHandlingPanelOKButton) {

var isDirectory = true;
//get the folder path
var firstURL = [[panel URLs] objectAtIndex:0];
//format it to a string
var file_path = [NSString stringWithFormat:@"%@", firstURL];

//remove the file:// path from string
if (0 === file_path.indexOf("file://")) {
file_path = file_path.substring(7);
}
}
//loop through the selected layers and export the XML
for(var i = 0; i < selection.count(); i++){
var layer = selection[i];
exportXML(layer, file_path);
}
}
};


Тепер для функції exportXML передамо 2 значення: виділений шар і file_path.

Спочатку ми налаштуємо XML:

//initialize the root element xml
var root = [NSXMLElement elementWithName:@"document"];
//initialize the xml object with the root element
var xmlObj = [[NSXMLDocument document] initWithRootElement:root];


Потім з переданого шару візьмемо потрібні змінні:

//create the variables
var layerName = layer.name();
var layerFrame = layer.absoluteRect();
var layerXpos = String(layerFrame.x());
var layerYpos = String(layerFrame.y());
var layerHeight = String(layerFrame.height());
var layerWidth = String(layerFrame.width());


Збережемо ці змінні в об'єкт XML:

//create the first child element and add it to the root
var layerElement = [NSXMLElement elementWithName:@"layer"];
[root addChild:layerElement];

//add elements based on variables to the first child
var layerNameElement = [NSXMLElement elementWithName:@"name" stringValue:layerName];
[layerElement addChild:layerNameElement];

var layerXPosElement = [NSXMLElement elementWithName:@"xPos" stringValue:layerXpos];
[layerElement addChild:layerXPosElement];

var layerYPosElement = [NSXMLElement elementWithName:@"yPox" stringValue:layerYpos];
[layerElement addChild:layerYPosElement];

var layerHeightElement = [NSXMLElement elementWithName:@"height" stringValue:layerHeight];
[layerElement addChild:layerHeightElement];

var layerWidthElement = [NSXMLElement elementWithName:@"width" stringValue:layerWidth];
[layerElement addChild:layerWidthElement];


І нарешті, запишемо XML-файл за переданим шляху файлу і повідомимо користувача, коли все буде зроблено:

//create the xml file
var xmlData = [xmlObj XMLDataWithOptions:NSXMLNodePrettyPrint];

//name the xml file the name of the layer and save it to the folder
[xmlData writeToFile:file_path+layerName+".xml"];
var alertMessage = layerName+".xml saved to: " + file_path;

alert("Layer XML Exported!", alertMessage);


Скрипт цілком виглядає так:

@import 'common.js'

var onRun = function(context) {
var doc = context.document;
var selection = context.selection;

//make sure something is selected
if(selection.count() == 0){
doc.showMessage("Please select a layer.");
}else{
//allow xml to be written to the folder
var fileTypes = [NSArray arrayWithObjects:@"xml", nil];

//create select folder window
var panel = [NSOpenPanel openPanel];
[panel setCanChooseDirectories:true];
[panel setCanCreateDirectories:true];
[panel setAllowedFileTypes:fileTypes];

//create variable to check if clicked
var clicked = [panel runModal];
//check if clicked
if (clicked == NSFileHandlingPanelOKButton) {

var isDirectory = true;
//get the folder path
var firstURL = [[panel URLs] objectAtIndex:0];
//format it to a string
var file_path = [NSString stringWithFormat:@"%@", firstURL];

//remove the file:// path from string
if (0 === file_path.indexOf("file://")) {
file_path = file_path.substring(7);
}
}
//loop through the selected layers and export the XML
for(var i = 0; i < selection.count(); i++){
var layer = selection[i];
exportXML(layer, file_path);
}
}
};

function exportXML(layer, file_path){

//initialize the root element xml
var root = [NSXMLElement elementWithName:@"document"];
//initialize the xml object with the root element
var xmlObj = [[NSXMLDocument document] initWithRootElement:root];

//create the variables
var layerName = layer.name();
var layerFrame = layer.absoluteRect();
var layerXpos = String(layerFrame.x());
var layerYpos = String(layerFrame.y());
var layerHeight = String(layerFrame.height());
var layerWidth = String(layerFrame.width());

//create the first child element and add it to the root
var layerElement = [NSXMLElement elementWithName:@"layer"];
[root addChild:layerElement];

//add elements based on variables to the first child
var layerNameElement = [NSXMLElement elementWithName:@"name" stringValue:layerName];
[layerElement addChild:layerNameElement];

var layerXPosElement = [NSXMLElement elementWithName:@"xPos" stringValue:layerXpos];
[layerElement addChild:layerXPosElement];

var layerYPosElement = [NSXMLElement elementWithName:@"yPos" stringValue:layerYpos];
[layerElement addChild:layerYPosElement];

var layerHeightElement = [NSXMLElement elementWithName:@"height" stringValue:layerHeight];
[layerElement addChild:layerHeightElement];

var layerWidthElement = [NSXMLElement elementWithName:@"width" stringValue:layerWidth];
[layerElement addChild:layerWidthElement];

//create the xml file
var xmlData = [xmlObj XMLDataWithOptions:NSXMLNodePrettyPrint];

//name the xml file the name of the layer and save it to the folder
[xmlData writeToFile:file_path+layerName+".xml"];
var alertMessage = layerName+".xml saved to: " + file_path;

alert("Layer XML Exported!", alertMessage);

}


Скачайте плагін або переглянути його на GitHub, протестуйте. Якщо ви створите прямокутник на артборде і експортується в XML, вийде приблизно так:
<document>
<layer>
<name>Rectangle</name>
<xPos>550</xPos>
<yPos>258</yPos>
<height>234</height>
<width>235</width>
</layer>
</document>


Експорт JSON
Збереження JSON-файлу реалізується майже також, крім пари моментів. Ми можемо використовувати першу частину скрипта і створити функцію exportJSON, яка виглядає так:

function exportJSON(layer, file_path){

//initialize the layer array
var layerArray = [];

//create the variables
var layerName = String(layer.name());
var layerFrame = layer.absoluteRect();
var layerXpos = String(layerFrame.x());
var layerYpos = String(layerFrame.y());
var layerHeight = String(layerFrame.height());
var layerWidth = String(layerFrame.width());

// add the strings to the array
layerArray.push({
name: layerName,
xPos: layerXpos,
yPos: layerYpos,
height: layerHeight,
width: layerWidth,
});

// Create the JSON object from the layer array
var jsonObj = { "layer": layerArray };
// Convert the object to a string json
var file = NSString.stringWithString(JSON.stringify(jsonObj, null, "\t"));
// Save the file
[file writeToFile:file_path+layerName+".json" atomically:true encoding:NSUTF8StringEncoding error:null];

var alertMessage = layerName+".json saved to: " + file_path;
alert("Layer JSON Exported!", alertMessage);

}


Скрипт цілком виглядає так:

@import 'common.js'

var onRun = function(context) {
var doc = context.document;
var selection = context.selection;

//make sure something is selected
if(selection.count() == 0){
doc.showMessage("Please select a layer.");
}else{
//allow xml to be written to the folder
var fileTypes = [NSArray arrayWithObjects:@"json", nil];

//create select folder window
var panel = [NSOpenPanel openPanel];
[panel setCanChooseDirectories:true];
[panel setCanCreateDirectories:true];
[panel setAllowedFileTypes:fileTypes];

var clicked = [panel runModal];
//check if Ok has been clicked
if (clicked == NSFileHandlingPanelOKButton) {
var isDirectory = true;
//get the folder path
var firstURL = [[panel URLs] objectAtIndex:0];
//format it to a string
var file_path = [NSString stringWithFormat:@"%@", firstURL];

//remove the file:// path from string
if (0 === file_path.indexOf("file://")) {
file_path = file_path.substring(7);
}
}
//loop through the selected layers and export the XML
for(var i = 0; i < selection.count(); i++){
var layer = selection[i];
exportJSON(layer, file_path);
}
}
};

function exportJSON(layer, file_path){

//initialize the layer array
var layerArray = [];

//create the variables
var layerName = String(layer.name());
var layerFrame = layer.absoluteRect();
var layerXpos = String(layerFrame.x());
var layerYpos = String(layerFrame.y());
var layerHeight = String(layerFrame.height());
var layerWidth = String(layerFrame.width());

// add the strings to the array
layerArray.push({
name: layerName,
xPos: layerXpos,
yPos: layerYpos,
height: layerHeight,
width: layerWidth,
});

// Create the JSON object from the layer array
var jsonObj = { "layer": layerArray };
// Convert the object to a string json
var file = NSString.stringWithString(JSON.stringify(jsonObj, null, "\t"));
// Save the file
[file writeToFile:file_path+layerName+".json" atomically:true encoding:NSUTF8StringEncoding error:null];

var alertMessage = layerName+".json saved to: " + file_path;
alert("Layer JSON Exported!", alertMessage);

}


Ви можете завантажити плагін тут або подивитися його на GitHub. Якщо протестуєте скрипт, отримаєте JSON-файл з таким вмістом:
{"layer":[{"name":"Rectangle","xPos":"550","yPos":"258","height":"234","width":"235"}]}


Висновок
За підсумком цієї частини ми навчилися:
  • Створювати модальне вікно для збереження файлу
  • Створювати об'єкти XML, JSON для зберігання змінних
  • Зберігати файли XML, JSON в певну папку.
Це лише підказка, як працювати з експортом даних з Sketch. Я тільки додав пару атрибутів для старту, але є величезну кількість даних, з яких можна вибрати для ваших цілей.

Спасибі, що читаєте наш урок, діліться своїми досягненнями в коментарях. Успіхів у створенні власних плагінів!

Удачі вам у створенні власних плагінів, діліться досягненнями під цим постом.

Про всі знайдені помилки, неточності перекладу і інших подібних речах прохання повідомляти в лічку.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.