Cómo construir un complemento de Sketch con JavaScript, HTML y CSS (Parte 2)

Publicado: 2022-03-10
Resumen rápido ↬ En esta segunda parte de nuestro tutorial sobre la creación de complementos de Sketch, continuaremos donde lo dejamos con la creación de nuestra interfaz de usuario, y luego pasaremos a la función clave de generar realmente nuestros mosaicos de capas y optimizando el código final del complemento.

Como se mencionó en la parte 1, este tutorial está destinado a personas que conocen y usan la aplicación Sketch y que tampoco tienen miedo de incursionar en el código. Para aprovecharlo al máximo, deberá tener al menos cierta experiencia básica escribiendo JavaScript (y, opcionalmente, HTML/CSS).

En la parte anterior de este tutorial, aprendimos sobre los archivos básicos que componen un complemento y cómo crear la interfaz de usuario del complemento. En esta segunda y última parte, aprenderemos cómo conectar la interfaz de usuario al código principal del complemento y cómo implementar las funciones principales del complemento. Por último, pero no menos importante, también aprenderemos cómo optimizar el código y la forma en que funciona el complemento.

Creación de la interfaz de usuario del complemento: hacer que nuestra interfaz web y el código del complemento de Sketch "hablen" entre sí

Lo siguiente que debemos hacer es configurar la comunicación entre nuestra interfaz web y el complemento Sketch.

Necesitamos poder enviar un mensaje desde nuestra interfaz web al complemento de Sketch cuando se hace clic en el botón "Aplicar" en nuestra interfaz web. Este mensaje debe informarnos qué configuraciones ha ingresado el usuario, como la cantidad de pasos, la cantidad de rotación, la cantidad de duplicados para crear, etc.

WKWebView facilita un poco esta tarea: podemos enviar mensajes a nuestro complemento Sketch desde el código JavaScript de nuestra interfaz web usando la API window.webkit.messageHandlers .

En el lado de nuestro código de Sketch, podemos usar otro método, addScriptMessageHandler:name: (o addScriptMessageHandler_name ) para registrar un controlador de mensajes que se llamará cada vez que reciba un mensaje enviado desde nuestra interfaz web de complemento.

Comencemos asegurándonos de que podamos recibir mensajes desde nuestra interfaz de usuario web. Dirígete a la función createWebView ui.js agrega lo siguiente:

 function createWebView(pageURL){ const webView = WKWebView.alloc().init(); // Set handler for messages from script const userContentController = webView.configuration().userContentController(); const ourMessageHandler = ... userContentController.addScriptMessageHandler_name( ourMessageHandler, "sketchPlugin" ); // Load page into web view webView.loadFileURL_allowingReadAccessToURL(pageURL, pageURL.URLByDeletingLastPathComponent()); return webView; };

Aquí usamos la propiedad userContentController de la vista web para agregar un controlador de mensajes que llamamos "sketchPlugin". Este "controlador de contenido de usuario" es el puente que garantiza que los mensajes se transmitan desde nuestra vista web.

¡Más después del salto! Continúe leyendo a continuación ↓

Es posible que haya notado algo extraño en el código anterior: ¡el objeto que estamos agregando como controlador de mensajes, ourMessageHandler , aún no existe! Desafortunadamente, no podemos simplemente usar un objeto o función de JavaScript normal como controlador, ya que este método espera un cierto tipo de objeto nativo.

Afortunadamente para nosotros, podemos sortear esta limitación usando MochaJSDelegate , una mini-biblioteca que escribí que hace posible crear el tipo de objeto nativo que necesitamos usando JavaScript normal. Deberá descargarlo manualmente y guardarlo en su paquete de complementos en Sketch/MochaJSDelegate.js .

Para usarlo, primero debemos importarlo a ui.js Agregue lo siguiente en la parte superior del archivo:

 const MochaJSDelegate = require("./MochaJSDelegate");

Ahora podemos usar MochaJSDelegate para crear el tipo de controlador de mensajes que addScriptMessageHandler:name: espera:

 function createWebView(pageURL){ const webView = WKWebView.alloc().init(); // Set handler for messages from script const userContentController = webView.configuration().userContentController(); const scriptMessageHandler = new MochaJSDelegate({ "userContentController:didReceiveScriptMessage:": (_, wkMessage) => { /* handle message here */ } }).getClassInstance(); userContentController.addScriptMessageHandler_name( scriptMessageHandler, "sketchPlugin" ); // Load page into web view webView.loadFileURL_allowingReadAccessToURL(pageURL, pageURL.URLByDeletingLastPathComponent()); return webView; };

El código que acabamos de agregar crea el objeto nativo que necesitamos. También define un método en ese objeto llamado userContentController:didReceiveScriptMessage: luego se llama a este método con el mensaje que queremos como segundo argumento. Dado que todavía no estamos enviando ningún mensaje, tendremos que volver aquí más tarde y agregar código para analizar y manejar los mensajes que recibimos.

A continuación, debemos agregar un código a nuestra interfaz web para enviarnos esos mensajes. Dirígete a /Resources/web-ui/script.js . Descubrirá que ya he escrito la mayor parte del código que maneja la recuperación de los valores de las <inputs /> HTML en las que el usuario ingresará sus opciones.

Lo que aún nos queda por hacer es agregar el código que realmente envía los valores a nuestro código de Sketch:

Encuentre la función de apply y agregue lo siguiente al final:

 // Send user inputs to sketch plugin window.webkit.messageHandlers.sketchPlugin.postMessage(JSON.stringify({ stepCount, startingOptions, stepOptions }));

Aquí usamos la API window.webkit.messageHandlers que mencionamos anteriormente para acceder al controlador de mensajes que registramos anteriormente como sketchPlugin . Luego, envíele un mensaje con una cadena JSON que contenga las entradas del usuario.

Asegurémonos de que todo esté configurado correctamente. Regrese a /Sketch/ui.js . Para asegurarnos de recibir los mensajes como esperamos, modificaremos el método que definimos anteriormente para que muestre un cuadro de diálogo cuando recibamos un mensaje:

 function createWebView(pageURL){ // ... const scriptMessageHandler = new MochaJSDelegate({ "userContentController:didReceiveScriptMessage:": (_, wkMessage) => { const UI = require("sketch/ui"); UI.alert("Hey, a message!", wkMessage.body()); } }).getClassInstance(); userContentController.addScriptMessageHandler_name( scriptMessageHandler, "sketchPlugin" ); // ... };

Ahora ejecute el complemento (es posible que primero deba cerrar cualquier ventana de mosaico existente que haya abierto), ingrese algunos valores y luego haga clic en "Aplicar". Debería ver una alerta como la siguiente: ¡esto significa que todo está conectado correctamente y nuestro mensaje se transmitió correctamente! De lo contrario, vuelva a los pasos anteriores y asegúrese de que todo se haya hecho como se describe.

Imagen que muestra el cuadro de diálogo que debería ver después de hacer clic en el botón "aplicar" en la interfaz de usuario del complemento.
El cuadro de diálogo que debería ver aparece una vez que haga clic en Aplicar. (Vista previa grande)

Ahora que podemos enviar mensajes desde nuestra interfaz a nuestro complemento, podemos pasar a escribir el código que realmente hace algo útil con esa información: generar nuestros mosaicos de capas.

Generación de mosaicos de capas

Hagamos un balance de lo que es necesario para que esto suceda. Simplificando un poco las cosas, lo que nuestro código necesita hacer es:

  1. Encuentra el documento actual.
  2. Encuentra la capa seleccionada del documento actual.
  3. Duplica la capa seleccionada (la llamaremos capa de plantilla ) x número de veces.
  4. Para cada duplicado, modifique su posición, rotación, opacidad, etc., según los valores específicos (cantidades) establecidos por el usuario.

Ahora que tenemos un plan razonable, sigamos escribiendo. Siguiendo con nuestro patrón de modularizar nuestro código, creemos un nuevo archivo, mosaic.js en la carpeta Sketch/ , y agreguemos el siguiente código:

 function mosaic(options){ }; module.export = mosaic;

Usaremos esta función como la única exportación de este módulo, ya que hace que una API sea más simple de usar una vez que la importamos; simplemente podemos llamar a mosaic() con las opciones que obtengamos de la interfaz web.

Los primeros dos pasos que debemos seguir son obtener el documento actual y luego su capa seleccionada. Sketch API tiene una biblioteca integrada para la manipulación de documentos a la que podemos acceder importando el módulo sketch/dom . Ahora solo necesitamos el objeto Document , así que lo sacaremos explícitamente. En la parte superior del archivo, agregue:

 const { Document } = require("sketch/dom");

El objeto Document tiene un método específico para acceder al documento actual que podemos usar, llamado getSelectedDocument() . Una vez que tenemos la instancia del documento actual, podemos acceder a las capas que el usuario haya seleccionado a través de la propiedad selectedLayers del documento. Sin embargo, en nuestro caso, solo nos preocupamos por las selecciones de una sola capa, por lo que solo tomaremos la primera capa que el usuario haya seleccionado:

 function mosaic(options){ const document = Document.getSelectedDocument(); const selectedLayer = document.selectedLayers.layers[0]; }; module.export = mosaic;

Nota: es posible que haya esperado que selectedLayers fuera una matriz, pero no lo es. En cambio, es una instancia de la clase Selection . Hay una razón para esto: la clase Selection contiene un montón de útiles métodos auxiliares para manipular la selección como clear, map, reduce y forEach. Expone la matriz de capas real a través de la propiedad de layer .

Agreguemos también algunos comentarios de advertencia en caso de que el usuario olvide abrir un documento o seleccionar algo:

 const UI = require("sketch/ui"); function mosaic(options){ const document = Document.getSelectedDocument(); // Safety check: if(!document){ UI.alert("Mosaic", "️ Please select/focus a document."); return; } // Safety check: const selectedLayer = document.selectedLayers.layers[0]; if(!selectedLayer){ UI.alert("Mosaic", "️ Please select a layer to duplicate."); return; } }; module.export = mosaic;

Ahora que hemos escrito el código para los pasos 1 y 2 (encontrar el documento actual y la capa seleccionada), debemos abordar los pasos 3 y 4:

  • Duplica la capa de plantilla x número de veces.
  • Para cada duplicado, modifique su posición, rotación, opacidad, etc., según los valores específicos establecidos por el usuario.

Comencemos extrayendo toda la información relevante que necesitamos de las options : la cantidad de veces que se duplica, las opciones de inicio y las opciones de pasos. Una vez más, podemos usar la desestructuración (como hicimos antes con Document ) para sacar esas propiedades de las options :

 function mosaic(options) { // ... // Destructure options: var { stepCount, startingOptions, stepOptions } = options; }

A continuación, desinfectemos nuestras entradas y asegurémonos de que el recuento de pasos sea siempre al menos 1:

 function mosaic(options) { // ... // Destructure options: var { stepCount, startingOptions, stepOptions } = options; stepCount = Math.max(1, stepCount); }

Ahora debemos asegurarnos de que la opacidad, la rotación, etc. de la capa de la plantilla coincidan con los valores iniciales deseados por el usuario. Dado que aplicar las opciones del usuario a una capa será algo que haremos mucho, moveremos este trabajo a su propio método:

 function configureLayer(layer, options, shouldAdjustSpacing){ const { opacity, rotation, direction, spacing } = options; layer.style.opacity = opacity / 100; layer.transform.rotation = rotation; if(shouldAdjustSpacing){ const directionAsRadians = direction * (Math.PI / 180); const vector = { x: Math.cos(directionAsRadians), y: Math.sin(directionAsRadians) }; layer.frame.x += vector.x * spacing; layer.frame.y += vector.y * spacing; } };

Y debido a que el espaciado solo debe aplicarse entre los duplicados y no en la capa de la plantilla, hemos agregado una marca específica, shouldAdjustSpacing , que podemos establecer en true o false dependiendo de si estamos aplicando opciones a una capa de la plantilla o no. De esa manera podemos asegurarnos de que la rotación y la opacidad se aplicarán a la plantilla, pero no el espaciado.

Volviendo al método de mosaic , ahora asegurémonos de que las opciones de inicio se apliquen a la capa de plantilla:

 function mosaic(options){ // ... // Configure template layer var layer = group.layers[0]; configureLayer(layer, startingOptions, false); }

A continuación, necesitamos crear nuestros duplicados. Primero, creemos una variable que podamos usar para rastrear cuáles son las opciones para el duplicado actual:

 function mosaic(options){ // ... var currentOptions; // ... }

Dado que ya aplicamos las opciones de inicio a la capa de la plantilla, debemos tomar las opciones que acabamos de aplicar y agregar los valores relativos de stepOptions para obtener las opciones que se aplicarán a la siguiente capa. Dado que también haremos esto varias veces más en nuestro ciclo, también moveremos este trabajo a un método específico, stepOptionsBy :

 function stepOptionsBy(start, step){ const newOptions = {}; for(let key in start){ newOptions[key] = start[key] + step[key]; } return newOptions; };

Después de eso, necesitamos escribir un ciclo que duplique la capa anterior, le aplique las opciones actuales, luego compense (o "escalone") las opciones actuales para obtener las opciones para el próximo duplicado:

 function mosaic(options) { // ... var currentOptions = stepOptionsBy(startingOptions, stepOptions); for(let i = 0; i < (stepCount - 1); i++){ let duplicateLayer = layer.duplicate(); configureLayer(duplicateLayer, currentOptions, true); currentOptions = stepOptionsBy(currentOptions, stepOptions); layer = duplicateLayer; } }

Todo listo: ¡hemos escrito con éxito el núcleo de lo que se supone que debe hacer nuestro complemento! Ahora, necesitamos conectar las cosas para que cuando el usuario realmente haga clic en el botón "Aplicar" se invoque nuestro código de mosaico.

Volvamos a ui.js y ajustemos nuestro código de manejo de mensajes. Lo que tendremos que hacer es analizar la cadena de opciones JSON que estamos obteniendo para que se conviertan en un objeto que realmente podamos usar. Una vez que tenemos estas opciones, podemos llamar a la función de mosaic con ellas.

Primero, analizar. Tendremos que actualizar nuestra función de manejo de mensajes para analizar el mensaje JSON que recibimos:

 function createWebView(pageURL){ // ... const scriptMessageHandler = new MochaJSDelegate({ "userContentController:didReceiveScriptMessage:": (_, wkMessage) => { const message = JSON.parse(wkMessage.body()); } }); }

A continuación, necesitaremos pasar esto a nuestra función de mosaic . Sin embargo, esto no es realmente algo que nuestro código en ui.js debería estar haciendo; se supone que debe preocuparse principalmente por lo que es necesario para mostrar cosas relacionadas con la interfaz en la pantalla, no por crear mosaicos en sí. Para mantener estas responsabilidades separadas, agregaremos un segundo argumento a createWebView que toma una función y llamaremos a esa función cada vez que recibamos opciones de la interfaz web.

Llamemos a este argumento onApplyMessage :

 function createWebView(pageURL, onApplyMessage){ // ... const scriptMessageHandler = new MochaJSDelegate({ "userContentController:didReceiveScriptMessage:": (_, wkMessage) => { const message = JSON.parse(wkMessage.body()); onApplyMessage(message); } }); }

También necesitaremos modificar nuestro método exportado, loadAndShow , para tomar este argumento onApplyMessage también y pasarlo a createWebView :

 function loadAndShow(baseURL, onApplyMessage){ // ... const webView = createWebView(pageURL, onApplyMessage); }

Finalmente, dirígete a main.js Ahora necesitamos importar nuestra función de mosaic y llamarla con las opciones que recibimos de la interfaz de usuario del complemento:

 const mosaic = require("./mosaic"); function onRun(context){ UI.loadAndShow(context.scriptURL, options => { mosaic(options); }); };

¡Ya casi hemos terminado!

Sin embargo, si ejecutamos nuestro código ahora y hacemos clic en el botón "Aplicar" en la interfaz del complemento, no sucederá nada. ¿Por qué? El motivo se debe a cómo se ejecutan los scripts de Sketch: de forma predeterminada, "viven" solo hasta que se llega al final de su script, después de lo cual Sketch lo destruye y libera los recursos que estaba usando.

Esto es un problema para nosotros, ya que significa que todo lo que necesitamos que suceda de forma asincrónica (en este caso, eso es después de llegar al final de nuestro código), como recibir mensajes, no puede, porque nuestro script ha sido destruido. ¡Esto significa que no recibiríamos ninguno de nuestros mensajes de la interfaz web ya que no estamos presentes para recibirlos y responderlos!

Hay una manera de indicarle a Sketch que necesitamos que nuestra secuencia de comandos se mantenga viva más allá de este punto, usando Fibers . Al crear una fibra, le decimos a Sketch que está sucediendo algo asíncrono y que necesita mantener nuestro script. Sketch solo destruirá nuestro script cuando sea absolutamente necesario (como cuando el usuario cierra Sketch o cuando el complemento Mosaic necesita ser actualizado):

 // ... const Async = require("sketch/async"); var fiber; function onRun(context){ if(!fiber){ fiber = Async.createFiber(); fiber.onCleanup(() => { UI.cleanup(); }); } UI.loadAndShow(context.scriptURL, options => { mosaic(options); }); };

¡Voila! Probemos nuestro complemento ahora. Con una capa seleccionada en Sketch, ingrese algunas configuraciones, luego haga clic en aplicar:

Probemos nuestro complemento ahora: con una capa seleccionada en Sketch, ingrese algunas configuraciones, luego haga clic en "Aplicar".

Mejoras finales

Ahora que tenemos implementada la mayoría de la funcionalidad de nuestro complemento, podemos intentar "alejarnos" un poco y echar un vistazo al panorama general.

Mejorar la experiencia del usuario

Si ha jugado con el complemento en su estado actual, es posible que haya notado que uno de los mayores puntos de fricción aparece cuando intenta editar un mosaico. Una vez que haya creado uno, debe presionar Deshacer, ajustar las opciones y luego hacer clic en 'Aplicar' (o presionar Intro). También hace que sea más difícil editar un Mosaico después de haber dejado su documento y haber vuelto a él más tarde, ya que su historial de deshacer/rehacer se habrá borrado, lo que le permitirá eliminar manualmente las capas duplicadas.

En un flujo más ideal, el usuario podría simplemente seleccionar un grupo de Mosaic, ajustar las opciones y ver la actualización de Mosaic hasta que obtenga el arreglo exacto que está buscando. Para implementar esto, tenemos dos problemas a resolver:

  1. Primero, necesitaremos una forma de agrupar los duplicados que forman un Mosaico. Sketch proporciona el concepto de Grupos, que podemos usar para resolver este problema.
  2. En segundo lugar, necesitaremos una forma de saber la diferencia entre un grupo normal creado por el usuario y un grupo Mosaico. La API de Sketch también nos brinda una forma de almacenar información en cualquier capa determinada, que podemos usar como una etiqueta de ruta y luego identificar un grupo como uno de nuestros grupos de mosaico 'especiales'.

Repasemos la lógica que escribimos en la sección anterior para abordar esto. Nuestro código original sigue los siguientes pasos:

  1. Encuentra el documento actual.
  2. Encuentra la capa seleccionada del documento actual.
  3. Duplica la capa seleccionada (la llamaremos capa de plantilla ) x número de veces.
  4. Para cada duplicado, modifique su posición, rotación, opacidad, etc., según los valores específicos (cantidades) establecidos por el usuario.

Para hacer posible nuestro nuevo flujo de usuarios, necesitamos cambiar estos pasos a:

  1. Toma el documento actual.
  2. Tome la capa seleccionada del documento actual.
  3. Determine si la capa seleccionada es un grupo de mosaico o no.
    • Si es alguna otra capa, utilícela como capa de plantilla y vaya al paso 4.
    • Si es un grupo Mosaico, considere la primera capa como la capa de plantilla y vaya al paso 5.
  4. Envuelva la capa de la plantilla dentro de un grupo y marque ese grupo como un grupo Mosaico.
  5. Quite todas las capas del interior del grupo excepto la capa de la plantilla.
  6. Duplica la capa de plantilla x número de veces.
  7. Para cada duplicado, modifique su posición, rotación, opacidad, etc., según los valores específicos establecidos por el usuario.

Tenemos tres nuevos pasos. Para el primer paso nuevo, el paso 3, crearemos una función llamada findOrMakeSpecialGroupIfNeeded que observará la capa que se le pasó para determinar si es o no un grupo Mosaic. Si es así, lo devolveremos. Dado que el usuario podría seleccionar una subcapa anidada en lo profundo de un grupo Mosaic, también necesitaremos verificar los padres de la capa seleccionada para saber si también son uno de nuestros grupos Mosaic:

 function findOrMakeSpecialGroupIfNeeded(layer){ // Loop up through the parent hierarchy, looking for a special group var layerToCheck = layer; while(layerToCheck){ if(/* TODO: is mosaic layer? */){ return layerToCheck; } layerToCheck = layerToCheck.parent; } };

Si no pudimos encontrar un grupo de Mosaico, simplemente envolvemos la capa que nos pasaron dentro de un Group , luego la etiquetamos como un grupo de Mosaico.

De vuelta en la parte superior del archivo, ahora también necesitaremos extraer la clase Group:

 const { Document, Group } = require("sketch/dom");
 function findOrMakeSpecialGroupIfNeeded(layer){ // Loop up through the parent hierarchy, looking for a special group var layerToCheck = layer; while(layerToCheck){ if(/* TODO: is mosaic layer? */){ return layerToCheck; } layerToCheck = layerToCheck.parent; } // Group const destinationParent = layer.parent; const group = new Group({ name: "Mosaic Group", layers: [ layer ], parent: destinationParent }); /* TODO: mark group as mosaic layer */ return group; };

Ahora tenemos que rellenar los huecos (todo's). Para empezar, necesitamos un medio para identificar si un grupo es o no uno de los grupos especiales que nos pertenecen o no. Aquí, el módulo de Settings de la biblioteca Sketch viene a nuestro rescate. Podemos usarlo para almacenar información personalizada en una capa en particular y también para leerla.

Una vez que importamos el módulo en la parte superior del archivo:

 const Settings = require("sketch/settings");

Luego podemos usar dos métodos clave que proporciona, setLayerSettingForKey y layerSettingForKey , para establecer y leer datos de una capa:

 function findOrMakeSpecialGroupIfNeeded(layer){ const isSpecialGroupKey = "is-mosaic-group"; // Loop up through the parent hierarchy, looking for a special group var layerToCheck = layer; while(layerToCheck){ let isSpecialGroup = Settings.layerSettingForKey(layerToCheck, isSpecialGroupKey); if(isSpecialGroup) return layerToCheck; layerToCheck = layerToCheck.parent; } // Group const destinationParent = layer.parent; layer.remove(); // explicitly remove layer from it's existing parent before adding it to group const group = new Group({ name: "Mosaic Group", layers: [ layer ], parent: destinationParent }); Settings.setLayerSettingForKey(group, isSpecialGroupKey, true); return group; };

Ahora que tenemos un método que maneja el ajuste de una capa en un grupo de mosaico (o, si ya es un grupo de mosaico, simplemente lo devuelve), ahora podemos conectarlo a nuestro método de mosaic principal justo después de nuestras comprobaciones de seguridad:

 function mosaic(options){ // ... safety checks ... // Group selection if needed: const group = findOrMakeSpecialGroupIfNeeded(selectedLayer); }

A continuación, agregaremos un bucle para eliminar todas las capas del grupo excepto la capa de plantilla (que es la primera):

 function mosaic(options) { // ... // Remove all layers except the first: while(group.layers.length > 1){ group.layers[group.layers.length - 1].remove(); } }

Por último, nos aseguraremos de que el tamaño del grupo se ajuste a su nuevo contenido, ya que el usuario podría haber seleccionado originalmente una capa anidada dentro del grupo anterior (una capa que podríamos haber eliminado).

También tendremos que asegurarnos de establecer la selección actual en nuestro propio grupo de mosaico. Esto asegurará que si el usuario está haciendo un montón de cambios rápidos en el mismo grupo de mosaico, no se deseleccionará. Después del código que ya escribimos para duplicar una capa, agregue:

 function mosaic(options) { // ... // Fit group to duplicates group.adjustToFit(); // Set selection to the group document.selectedLayers.clear(); group.selected = true; }

Pruebe el complemento de nuevo. ¡Debería encontrar que editar un mosaico es mucho más fluido ahora!

Mejorar la interfaz

Otra cosa que puede notar es la falta de sincronización entre la ventana de visualización y la interfaz dentro de ella, en términos de que ambos se vuelven visibles al mismo tiempo. Esto se debe al hecho de que cuando mostramos la ventana, no se garantiza que la interfaz web haya terminado de cargarse, por lo que a veces "aparecerá" o "parpadeará" después.

Una forma de solucionar esto es escuchar cuándo la interfaz web ha terminado de cargarse y solo entonces mostrar nuestra ventana. Hay un método, webView:didFinishNavigation: , que WKWebView llamará cuando la página actual haya terminado de cargarse. Podemos usarlo para obtener exactamente la notificación que estamos buscando.

De vuelta en ui.js , extenderemos la instancia de MochaJSDelegate que creamos para implementar este método, que a su vez llamará al argumento onLoadFinish que pasaremos a createWebView :

 function createWebView(pageURL, onApplyMessage, onLoadFinish){ const webView = WKWebView.alloc().init(); // Create delegate const delegate = new MochaJSDelegate({ "webView:didFinishNavigation:": (webView, navigation) => { onLoadFinish(); }, "userContentController:didReceiveScriptMessage:": (_, wkMessage) => { const message = JSON.parse(wkMessage.body()); onApplyMessage(message); } }).getClassInstance(); // Set load complete handler webView.navigationDelegate = delegate; // Set handler for messages from script const userContentController = webView.configuration().userContentController(); userContentController.addScriptMessageHandler_name(delegate, "sketchPlugin"); // Load page into web view webView.loadFileURL_allowingReadAccessToURL(pageURL, pageURL.URLByDeletingLastPathComponent()); return webView; };

Y de vuelta en el método loadAndShow , lo ajustaremos para que solo muestre la ventana una vez que se haya cargado la vista web:

 function loadAndShow(baseURL, onApplyMessage){ // ... const window = createWindow(); const webView = createWebView(pageURL, onApplyMessage, () => { showWindow(window); }); window.contentView = webView; _window = window; };

¡Bingo! Ahora nuestra ventana se muestra solo cuando la vista web ha terminado de cargarse, evitando ese molesto parpadeo visual.

Conclusión

¡Felicitaciones, ha creado su primer complemento de Sketch!

Si desea instalar y jugar con Mosaic, puede descargar el complemento completo desde GitHub. Y antes de partir, aquí hay algunos recursos que pueden ser útiles durante el resto de su viaje:

  • developer.sketchapp.com El recurso oficial sobre el desarrollo del complemento de Sketch. Contiene varias guías útiles, así como una referencia de API para la biblioteca JavaScript de Sketch.
  • sketchplugins.com Una fantástica y útil comunidad de desarrolladores de complementos de Sketch. Excelente para obtener respuestas a todas sus preguntas candentes.
  • github.com/sketchplugins/plugin-directory Repositorio oficial y central de GitHub de complementos de Sketch. ¡Puede enviar sus complementos aquí y compartirlos con el resto de la comunidad de Sketch!