📜 ⬆️ ⬇️

A beginner's guide to the development of plug-ins for the graphic editor Sketch



Greetings friends! My name is Anton, I am developing a website ux.pub dedicated to the graphic editor Sketch. Very often I receive questions about the subtleties of developing plug-ins for Sketch. I'm not a developer or plugin developer, so I decided to translate the most detailed plugin guide from Mike Mariano.

Part 1 - How to start?


Do you want to start writing sketch plugins and don’t know where to start? Keep reading, since this post is just for you!
')
Master the base is not so easy. There are plenty of examples of existing plug-ins, but it is very difficult to understand where to start. To help you, I gathered all the necessary information that I could find in one place.

Of course, this is not a manual for writing plug-ins for advanced, since I myself am not a developer. I'm a UI / UX designer who sometimes has to code at a pretty good level (at least, I think so). Tru-programmers openly cry, seeing my code, but I think that just such code is well understood by beginners.

If you are an engineer who is looking for more complex examples, this post will be useful to you, as well as the official website of Sketch-developers.

Why write a plugin?


Plug-ins are ideal for automating repetitive tasks, they greatly simplify the work and production. There is every chance that there is already a plug-in for your needs, so first look for a ready-made solution before writing your own. It is very likely that someone has already done the task better than you. But if you have a unique workflow for creating a UI (as I do with the design of game interfaces in Unity), you will most likely need a custom solution.

Start


Before you proceed to the code, install all the necessary programs and bookmarks.

  1. Install a text editor if you do not already have it. (I use Atom , but there are many other great editors, such as Sublime or Textmate ).

  2. Open the Debug Console, and add it to your Dock, you will often use it.>

    1-8tsgn6bxwkezgshwyip8lw

  3. The console is used by your machine for TOTAL debug, so create a new query log filter: Sketch: File> New System Query Log

Copy these settings and click Ok.

1-n8clsi0_jhe-4jsfx-txga

Sketch filter will appear in the left column.

1-p0oatbbn78yfdssskqrigg

4. Bookmark the Sketch Plugins folder for quick access by adding it to Favorites in the Finder window.

You will also frequently access this folder, so it is advisable to have it at hand.

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

1-xermiwsh60b0osekwabugw

That's it, you're ready to write your first plugin!

Creating a plugin in 10 easy steps


Sketch plugins are folders with the .sketchplugin extension that are easy to share with other users.

In this example, we will create a basic script to get the name of the page. You do not need to know programming to accomplish this task, but it will help you understand the basic principles. In subsequent posts, I will document different scripts for getting different data from Sketch and changing them. This example is the simplest.

Sketch plugins are written in CocoaScript, which is a mixture of Objective-C / Cocoa and JavaScript. I am quite familiar with Javascript, so there was no difficulty. Not to say that I'm in CocoaScript, like a fish in water, but my knowledge of JavaScript was enough to figure it out.

So, let's begin!

1. Create a new folder in the Sketch Plugins directory and name it MyPlugin.sketchplugin

1-dw5qn1na_zd8dgzm6s141w

(as soon as you add the .sketchplugin extension, double-click on it to start installing the plugin instead of opening the folder. To open the folder itself, right-click on the plugin and select the Show Package Contents option).

2. Inside the folder, create another folder and name it Contents

3. Inside the Contents, create a Sketch folder.

The final structure of the plug-in folder will look like this:

1-mnhkmqkpjyg0taofttwtgw

Inside the Sketch folder you will create the plugin itself, which consists of at least 2 files - a manifest and a script.

1-8l57idwssf5tnot0x408tq

The manifest describes the plugin and may contain different hot keys and additional scripts, it is always called manifest.json.

The script contains the plugin code itself, and the manifest refers to it. The name can be changed at its discretion, but it must be the same in both files.

4. In a text editor, create a new file called manifest.json and save it to MyPlugin.sketchplugin> Contents> Sketch

5. Copy and paste this code into manifest.json, and save.

{ "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" } ] } 

Now MyPlugin will appear in your Sketch plugins menu. You can change the name of the plugin or the hot key of its call, and this will be reflected in the Plugins menu in Sketch. It is important to choose a launch key that is not assigned to other installed applications or plug-ins, otherwise it will not work.

Now create MyScript.js referenced by manifest. Make sure that the file name matches the name in the manifest file!

6. Return to the text editor and create a new file called MyScript.js, and also save it to the MyPlugin.sketchplugin> Contents> Sketch folder.

7. Copy and paste this code into 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); } } 


I will explain this code in more detail in the following sections. For now, rely on the comments in the lines.

8. Go to Sketch and open a new file.

9. In the Plugins menu, select MyPlugin> Get Page Names

1-uelcrvjw2vyxzgjdib58ya

10. Go to the console and at the bottom of the log you should see the page title:

 10:54:42 PM Get Page Names (Sketch Plugin): Page 1 

Try changing the page name in the sketch file and restart the plugin. The log should show the new name. Add another page and rename it, and then launch the plugin, the console will now show the names of both pages.

That's all!


I’ve been working in Sketch for only a few weeks, and I’m already impressed with its power and customization capabilities. You can download the current version of the plugin here .

Part 2 - User Notifications


I discovered the excellent Atom text editor, which I switched to. I don’t know why I haven’t used it yet, but now I said goodbye to my good old TextMate!

In the previous chapter, we worked on a plugin that showed the names of all the pages of your document in the Console. Now let's explore other ways to display information. They are needed if you want to notify users (and yourself) about different things, for example, that the plug-in has worked, or that the user needs to enter some data. Also, you do not have to constantly switch to the console to test your scripts.

There are two additional ways to notify users inside Sketch:

  1. Message (Message) - a short, unobtrusive message that is displayed at the bottom of the application, hides after a short period of time
  2. Alert Box (Alert Box) - a standard pop-up window, which either prompts the user to enter data, or requires some action to continue.

Each method has its own use cases, and usually you don’t have to overload the plugin (and users) with a lot of messages. Experiment with both to figure out which method is best for your own plugin.

Let's return to the plugin script from the first example and redo it.

 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); } } 

If we look at this piece of code, pageName refers to the name of each page in the page document array. This is a data container containing all the information about the page. Typically, these arrays are accessed using a for loop, which iterates through each object to retrieve or assign specific values.

In this line, the pageName variable is sent to the Console.

 log(pageName); 

Now add a message to the bottom of the script that will display the message inside Sketch when the script runs.

To do this, we add a line of code after the for loop of the for loop:

 doc.showMessage(“MyPlugin Finished!”); 

Doc is a variable at the top of the script that references the document, and showMessage is a built-in function in which we can pass a variable or string (String) to display a message.

Here is how to add it after the for loop in the context of the whole script:

 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!"); } 

Run MyPlugin via the Plugins menu to see the result. At the bottom of the Sketch window you should see:

1-z9cmbfc9syuokwrzdsairq

This type of message is useful if you do not want to constantly switch to the console, or if there is something that you would like to show to the user, but that does not require his participation.

If you want a more prominent message that requires some action from the user, it is better to use the alert box. To add it, add a line of code to the beginning of the script, and one more after displaying previous messages.

The first line is added up, above the declaration of the doc variable, to refer to the application itself:

 var app = [NSApplication sharedApplication]; 

The second line gives access to the new variable to send messages to the application.

 [app displayDialog:"This is an alert box!" withTitle:"Alert Box Title"]; 

And this is what the final code looks like with the new lines:

 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"]; } 

Start MyPlugin from the Plugin menu, and you should see something like this:

1-1aqb4gdfoh7dxzdugdqdzq

Change the messages (in quotes) for anything and restart the plugin. Full customization!

This is the very beginning of debugging and displaying data inside Sketch. You can try to change message / notification scripts, add additional scripts to display different data - variables or array counters.

To show the number of pages in a document, you can create a message displaying pages.count ().

 [app displayDialog:”This document has “ + pages.count() + “ pages.” withTitle:”Alert Box Title”]; 

Here is the result:

1-ruhpur9ob2ns-kknochjyq

We have dealt with custom notifications! First, we prepared an environment for development, then figured out how to debug and display messages inside Sketch. Next, we focus more on the code itself, searching for specific data and how to process it.

The current version of the plugin can be downloaded here .

Part 3 - Writing code for reusable


It is time to deploy monitors vertically, because we are starting to program!

In this part, we will cover the basic concepts of writing code, creating functions for multiple calls, and linking two scripts together. If you are already familiar with writing scripts, you can simply scroll down to the end of this part, download the source code and redo them in your own way. If you are new to this business, or you need to refresh your knowledge, keep reading!

Basic code writing concepts


There is a code that we have already written, and it includes variables, arrays, functions, and for loops. These elements, as well as the if / else statements, are the fundamental building blocks of everything I do. As soon as I mastered these elements (especially the loop), it became much easier for me to understand the scripts. I write my scripts in Javascript and Objective-C, but these concepts are quite universal for any programming language. You just need to work out a certain way of writing code.

I will briefly explain these concepts:

A variable is a link to some type of information. This can be either a string (a piece of text), or a number or a boolean value (true or false). This is a hot key to access the value, and it is very convenient to use it instead of typing it again and again.

 var myName = “Mike”; var thisYear = 2016; var givesHighFives = true; 

If I set myName to “Mike”, then I can simply write everywhere in the myName script, and this variable will refer to “Mike”. But if I want the value of myName to become “Shawn”, I will need to change it only once in the declaration of the variable, and each of its essence in the code will also change.

An array is also a variable, but usually it contains several values ​​of the same type. As if you had an array of days of the week, you would see the string value of all the days inside such an array.

 var daysOfTheWeek = {“Sunday”, “Monday”, “Tuesday”, Wednesday”, “Thursday”, “Friday”, “Saturday”}; 

You can also write it so it will be easier to see the number of elements in the array that start with 0:

 var daysOfTheWeek[]; daysOfTheWeek[0] = “Sunday”; daysOfTheWeek[1] = “Monday”; daysOfTheWeek[2] = “Tuesday”; daysOfTheWeek[3] = “Wednesday”; daysOfTheWeek[4] = “Thursday”; daysOfTheWeek[5] = “Friday”; daysOfTheWeek[6] = “Saturday”; 

A function is a reusable piece of code that performs a specific task. Usually you ask them if you need to do something over and over. You can pass variables to it, force them to return a value, or keep them empty.

 var eyesOpen = true; function closeEyes(){ eyesOpen = false; } function openEyes(){ eyesOpen = true; } 

In the example above, each time you want to close your eyes, you can call the closeEyes () function, then call openEyes () to open them.

The if / else statement does exactly what it says, i.e. checks some condition, performs a certain action if the condition is fulfilled, otherwise performs something else.

 if(daysOfTheWeek == “Sunday”){ watchGameOfThrones(); }else if(daysOfTheWeek == “Wednesday”){ watchMrRobot(); }else{ watchTheNews(); } 

The for loop is used to repeat a specific block of code a known number of times. For example, in the daysOfTheWeek array, if you wanted to list the values ​​of an array in the list, you would use a for loop for this.

Without a loop, you would write:

 log(daysOfTheWeek[0]); log(daysOfTheWeek[1]); log(daysOfTheWeek[2]); log(daysOfTheWeek[3]); log(daysOfTheWeek[4]); log(daysOfTheWeek[5]); log(daysOfTheWeek[6]); 

And with the cycle you would have to write only:

 for(var i = 0; i < daysOfTheWeek.count(); i++){ log(daysOfTheWeek[i]; } 

Much easier, right? For the loop, we set an initial value of i equal to 0 and counted until i reaches the number of values ​​in the daysOfTheWeek array, i.e. 7. That is, starting at i = 0, the loop logs the value of daysOfTheWeek, then adds 1 to i, and everything repeats. In total, this is done 7 times, until a complete set of values ​​in the array is reached.

Creating an alert window function (Alert Box)


Let's go back to the MyPlugin code. In the previous section, we created an alert window that was displayed after the script was processed.

 [app displayDialog:”This is an alert box!” withTitle:”Alert Box Title”]; 

It looks like something that will be used every time we need to show messages to the user, and I don’t really want to prescribe the entire line of code for each such message each time. This is especially true if the displayed message is a combination of variables and strings, the code can be quite long.

An excellent reason to make a reusable function, and for this you need to register:

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

Each time we call alert () and pass the header and message, an alert box will be displayed.

Add this function to the end of the script, after the parentheses of the onRun function:

 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]; } 

And now we can change the code written earlier, replacing the original reference to the variable with it, since it is now registered in the function. The script now looks like this:

 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]; } 

Run the plugin, and you will see the same result as before, but now your code refers to the function. It can be called anywhere in the script, and you will not need to rewrite the code again and again. But what if we had another script in which to use this function?

Link to scripts from other scripts


And here it is appropriate to create a library. Sometimes you have a common function that should not be tied to a single script. As in the case of the alert () function, or with the function of rounding the number to the nearest integer. These are common functions that will most likely be used in different scripts. Therefore, instead of copy-paste functions in each file, you can create a common file, and insert it as needed.

1. Save the new JavaScript file to the same folder where MyScript.js is stored and name it common.js. Now there will be 3 files in the folder:

1-c9uz7wbojajdje_xvd0ziw

2. Cut the alert function from MyScript.js and paste it into common.js and save the changes.

In common.js there should be only this code:

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


3. To run this script from MyScript.js, add this line of code at the top of the script and save:

 @import 'common.js' 

The whole script with a new line at the top and the remote alert () function looks like this:

 @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.") } 

Run the plugin again, it should work as before, but now you run the function from the common script! Optimization is complete!

Now you have the basics of writing and understanding more complex plugins. Next, we examine access to the data of a Sketch document through its variables and arrays, ways of passing through these arrays to extract and assign the values ​​we need.

You can download the current version of the plugin here .

Part 4 - Examples from the real world


We have already learned how to organize an environment for writing and debugging code, how to display different types of user notifications, and also have mastered the basic concepts of writing code. It seems we have everything to start writing our own plugins.

Fortunately, Bohemian Coding have documentation on their classes:
http://developer.sketchapp.com/reference/class/

Unfortunately, as it is written at the beginning of this page, it is still “under development”. So, not everything has been described, and there are not many examples of what to do with this.

Personally, I learn best from examples. Understanding the code can be very difficult, but when you see how it is used in relation to your knowledge and goals, it becomes much easier. I will give three examples that I found while working with Sketch, I hope they will be useful to you!

Example 1: Switching the height and width of the artboard.


Strange, but in the standard Sketch functionality there is no change in the orientation of the artboard from portrait to landscape and vice versa. I searched, and did not find a single plug-in that solves this problem, so it's time to create our own!

For clarity, let's define what we will do. This helps determine the goal, ways to achieve it, as well as identify extreme cases that will be faced. It's almost like creating a UX script, right?

For this plugin, we will write a script that does the following:


Adding another script to MyPlugin


Instead of adding a new piece of code to MyScript.js, let's create a new script called RotateArtboard.js, and save it in the MyPlugin.sketchplugin folder.

Add this code to RotateArtboard.js. Each main script should be in the onRun function, so this script is always a good starting point:

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

You will see that we are importing the common.js file to use the same alert function that we have already created.

At the moment in the plugin folder should be the following files:

1-zq4ryj_udiwcr-biqkupkg

Now open manifest.json to add another script to our manifest.

Copy the code snippet with all the MyScript.js commands, add a comma after the MyScript block, and insert in front of the closing commands bracket, like this:

 { "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" } ], } 

Now you see the new script in the MyPlugin menu (you can change the hotkeys, by the way):

1-hv9u76wafmq1rnjitphdg

Let's go back to the code! To find out what is highlighted, use the following:

 var selection = context.selection; 

This code creates an array type variable with all selected layers. Now we can check this array to find out if there is anything inside it. If there is 0, then nothing is selected, and we will tell the user to select something.

 if(selection.count() == 0){ doc.showMessage(“Please select something.”); } 

But if the counter is not 0, then there is something inside the array, at least one layer should be selected. Now we can cycle through selected layers to determine if there are artboards among them ( 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 width variable 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:


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

:



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, :



Conclusion


, 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, !

findings


, :



6 —


Sketch svg css . -, , . , .

- (Zeplin, Avocode, Sketch Measure, Sympli .), . , . : - Sketch, “”, . , .

, , , - . : , , .

, . , .

Sketch — ,


, . , , ? — XML JSON. Sketch , , , , , . , , XML JSON ?

, Sketch. , XML JSON. , . :

  1. .
  2. (, X, Y, ).
  3. XML, JSON-.
  4. , , 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 xml element 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 xml element 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 json string 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 json string 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"}]} 

Conclusion


:


, Sketch. , , .

, , . !

, .

, .

Source: https://habr.com/ru/post/310968/


All Articles