Arrastra y suelta en tus aplicaciones iOS



El mecanismo de Drag & Drop que se ejecuta en iOS 11 y iOS 12 es una forma de copiar o mover datos de forma asíncrona de forma gráfica tanto en una sola aplicación como entre diferentes aplicaciones. Aunque esta tecnología tiene aproximadamente 30 años, se ha convertido literalmente en una tecnología "innovadora" en iOS debido al hecho de que al arrastrar algo en iOS , multitouch permite interactuar libremente con el resto del sistema y recopilar datos para restablecer desde diferentes aplicaciones.

iOS hace posible capturar múltiples elementos a la vez. Además, no tienen que estar en una accesibilidad conveniente para la selección: puede tomar el primer objeto, luego ir a otra aplicación y tomar otra cosa: todos los objetos se recogerán en una "pila" debajo de su dedo. Luego llame al dock universal en la pantalla, abra cualquier aplicación allí y capture el tercer objeto, y luego vaya a la pantalla con las aplicaciones ejecutándose y, sin liberar los objetos, volcarlos en uno de los programas abiertos. Tal libertad de acción es posible en el iPad , en el iPhone , la cobertura de Drag & Drop en iOS limita al marco de una aplicación.

Las aplicaciones más populares ( Safary , Chrome , IbisPaint X , Mail , Photos , Files , etc.) ya tienen un mecanismo de Drag & Drop . Además de esto, Apple proporcionó a los desarrolladores una API muy simple e intuitiva para integrar el mecanismo de Drag & Drop en su aplicación. El mecanismo de Drag & Drop , al igual que los gestos, funciona en UIView y utiliza el concepto de interacciones , un poco como los gestos, por lo que puede pensar en el mecanismo de Drag & Drop como un gesto realmente poderoso.

Es, además de gestos, muy fácil de integrar en su aplicación. Especialmente si su aplicación usa la tabla UITableView o la colección UICollectionView , porque para ellos API Drag & Drop mejora y se eleva a un mayor nivel de abstracción en el sentido de que la Collection View sí misma lo ayuda con el indexPath del elemento de la colección que desea arrastrar y soltar Drag Ella sabe dónde está su dedo y lo interpreta como el indexPath del elemento de colección que está "arrastrando" Drag en este momento, o como el indexPath del elemento de colección donde está "soltando" Drop algo. Por lo tanto, la Collection View colección le proporciona indexPath , pero de lo contrario es absolutamente la misma API Drag & Drop que para una vista general de UIView .

El proceso de Drag & Drop en iOS tiene 4 fases diferentes:

Ascensor


Levantar (levantar): es cuando el usuario realiza un gesto de presión prolongado , que indica el elemento que se "arrastrará y soltará". En este momento, se forma una llamada " lift preview " muy ligera del elemento indicado, y luego el usuario comienza a arrastrar los dedos.



Arrastrar (arrastrar y soltar)


Arrastrar (arrastrar y soltar): esto es cuando el usuario mueve el objeto en la superficie de la pantalla. Durante esta fase, se puede modificar la " lift preview " para este objeto (aparece un signo más "+" verde u otro signo) ...



... también se permite cierta interacción con el sistema: puede hacer clic en otro objeto y agregarlo a la sesión actual de "arrastrar y soltar":



Soltar


La caída ocurre cuando el usuario levanta un dedo. En este momento, pueden suceder dos cosas: el objeto Drag se destruirá o el objeto Drop se "soltará" en el destino.



Transferencia de datos


Si no se canceló el proceso de Arrastrar " arrastrar y soltar" y se realizó el restablecimiento de " Soltar ", se produce la Transferencia de datos (transferencia de datos), en la cual el "punto de soltar" solicita datos de la "fuente" y se produce una transferencia de datos asincrónica.

En este tutorial, le mostraremos cómo integrar fácilmente el mecanismo de Drag & Drop en su aplicación iOS usando la aplicación de demostración de la Galería de imágenes, tomada del curso de tareas CS193P de Stanford .
Dotaremos a la Collection View capacidad de llenarnos con imágenes EXTERIORES, así como reorganizar elementos INTERIORES utilizando el mecanismo de Drag & Drop . Además, este mecanismo se usará para volcar elementos innecesarios de la Collection View en un "bote de basura", que es una vista de UIV normal y se representa mediante un botón en el panel de navegación. También podemos compartir imágenes recopiladas en nuestra Galería con otras aplicaciones utilizando el mecanismo de Drag & Drop , por ejemplo, Notes o Notes o Mail o una biblioteca de fotos ( Photo ).

Pero antes de centrarme en la implementación del mecanismo de Drag & Drop en la aplicación de demostración "Galería de imágenes", examinaré brevemente sus componentes principales.

Características de la aplicación de demostración "Galería de imágenes"


La interfaz de usuario ( UI ) de la aplicación Galería de imágenes es muy simple. Este es un "fragmento de pantalla" del Image Gallery Collection View Controller de la Image Gallery Collection View Controller insertado en el Navigation Controller :



La parte central de la aplicación es, sin duda, el Image Gallery Collection View Controller , que es compatible con la clase ImageGalleryCollectionViewController con el modelo de galería de imágenes como variable var imageGallery = ImageGallery () :



El modelo está representado por una estructura de estructura ImageGallery que contiene un conjunto de imágenes de imágenes , en el que cada imagen se describe mediante una estructura de estructura ImageModel que contiene la URL ubicación URL imagen (no vamos a almacenar la imagen en sí) y su relación de aspecto:



Nuestra ImageGalleryCollectionViewController implementa el protocolo DataSource :



La celda personalizada en la colección de celdas contiene una imagen imageView: UIImageView! e indicador de actividad de la ruleta: UIActivityIndicatorView! y es compatible con la subclass personalizada ImageCollectionViewCell de la clase UICollectionViewCell :



Public API de la clase ImageCollectionViewCell es la URL de la imagen imageURL . Tan pronto como lo instalamos, nuestra UI actualiza, es decir, los datos de la imagen se seleccionan de forma asíncrona en esta imagen URL y se muestran en la celda. Mientras se recuperan datos de la red, el indicador de actividad de la ruleta está funcionando, lo que indica que estamos en el proceso de recuperar datos.

Utilizo la cola global (qos: .userInitiated) con el argumento de calidad de servicio qos para obtener datos en una URL determinada, que se establece en .userInitiated porque selecciono los datos a solicitud del usuario:



Cada vez que usa sus propias variables dentro de un cierre, en nuestro caso es imageView e imageURL , el compilador lo obliga a colocarse frente a ellas . para que te preguntes: "¿Hay un" enlace cíclico de memoria "?" Aquí no tenemos un " memory cycle " explícito, porque el yo mismo no tiene un puntero a este cierre.

Sin embargo, en el caso de subprocesos múltiples, debe tener en cuenta que las celdas en la Collection View son reutilizables gracias al método dequeueReusableCell . Cada vez que aparece una celda (nueva o reutilizada) en la pantalla, la imagen se descarga de la red de forma asincrónica (en este momento, la " rueda giratoria " del indicador de actividad de la rueda gira).

Tan pronto como se completa la descarga y se recibe la imagen, se actualiza la UI esta celda de colección. Pero no esperamos a que se cargue la imagen, seguimos desplazándonos por la colección y la celda de colección que marcamos sale de la pantalla sin actualizar nuestra UI . Sin embargo, debe aparecer una nueva imagen a continuación y la misma celda que ha salido de la pantalla se reutilizará, pero para otra imagen, que puede cargar y actualizar rápidamente la UI . En este momento, la carga de la imagen iniciada previamente en esta celda volverá y la pantalla se actualizará, lo que conducirá a un resultado incorrecto. Esto se debe a que ejecutamos diferentes cosas que funcionan con la red en diferentes hilos. Vuelven en diferentes momentos.

¿Cómo podemos solucionar la situación?
Dentro del marco del mecanismo GCD que utilizamos, no podemos cancelar la descarga de la imagen de la celda que salió de la pantalla, pero podemos, cuando nuestros datos imageData llegan de la red, verificar la URL URL que causó la carga de estos datos y compararla con la que el usuario quiere tener en esta celda en este momento, es decir, imageURL . Si no coinciden, no actualizaremos la celda de la UI y esperaremos los datos de imagen que necesitamos:



Esta línea de código aparentemente absurda url == self.imageURL hace que todo funcione correctamente en un entorno multiproceso que requiere una imaginación no estándar. El hecho es que algunas cosas en la programación multiproceso ocurren en un orden diferente al que está escrito el código.

Si no fue posible seleccionar los datos de la imagen, se genera una imagen con un mensaje de error en forma de cadena "Error" y un emoji con "ceño fruncido". Solo el espacio vacío en nuestra Collection View puede confundir un poco al usuario:



No queremos que la imagen con el mensaje de error repita el aspecto de esta imagen de error, porque en este caso el texto junto con el emoji se estirará o comprimirá. Nos gustaría que fuera neutral - cuadrado, es decir, tendría una relación de aspecto de aspecto cercana a 1.0.



Debemos informar a nuestro Controller de este deseo para que corrija la relación de aspecto de la relación de aspecto para la ruta de índice correspondiente en su modelo de galería de imágenes . Esta es una tarea interesante, hay muchas formas de resolverla, y elegiremos la más fácil de ellas, usando el cierre opcional var closAspectRatio: (() -> Void)? . Puede ser igual a cero y no necesita ser instalado si esto no es necesario:



Al llamar al cierre changeAspectRatio? () En caso de recuperación de datos errónea, uso la cadena Opcional . Ahora, cualquier persona que esté interesada en algún tipo de configuración al recibir una imagen errónea puede establecer este cierre en algo específico. Y esto es exactamente lo que hacemos en nuestro Controller en el método cellForItemAt :



Los detalles se pueden encontrar aquí .

Para mostrar imágenes con el correcto AspectRatio , se utiliza el método sizeForItemAt del delegado UICollectionViewDelegateFlowLayout :



Además de la Collection View imágenes Collection View , en nuestra UI colocamos un Bar Button en el panel de navegación con una imagen personalizada de GarbageView que contiene una "papelera" como una subvista :



En esta figura, los colores de fondo para GarbageView y el botón UIButton con la imagen del "bote de basura" (en realidad hay un fondo transparente) se modifican especialmente para que vea que el usuario que "volca" las imágenes de la Galería en el "bote de basura" mucho más espacio para maniobrar al "soltar" Soltar que solo el ícono de la papelera.
La clase GarbageView tiene dos inicializadores y ambos usan el método setup () :



En el método setup () , también agrego myButton como una subvista con la imagen del "bote de basura" tomado del Bar Button estándar del Bar Button :



Configuré un fondo transparente para GarbageView :



El tamaño del bote de basura y su posición se determinarán en el método layoutSubviews () de la clase UIView , dependiendo de los límites de la UIView dada:



Esta es la versión inicial de la aplicación de demostración "Image Gallery", se encuentra en Github en la carpeta ImageGallery_beginning . Si ejecuta esta versión de la aplicación "Galería de imágenes", verá el resultado de la aplicación trabajando en los datos de prueba, que luego eliminaremos y completaremos la "Galería de imágenes" exclusivamente FUERA:



El plan para implementar el mecanismo de Drag & Drop en nuestra aplicación es el siguiente:

  1. primero, dotaremos a nuestra Collection View capacidad de "arrastrar" Drag DE ella UIImage imágenes tanto externa como localmente,
  2. luego le enseñaremos a nuestra colección de imágenes de Collection View de Collection View para aceptar "arrastrar" Drag externa o localmente desde UIImage ,
  3. también le enseñaremos a nuestro GarbageView con el botón del bote de basura para aceptar imágenes UIImage arrastradas desde la Collection View local y eliminarlas de la Collection View


Si va al final de este tutorial y completa todos los cambios de código necesarios, recibirá la versión final de la aplicación de demostración "Galería de imágenes", en la que se implementó el mecanismo de Drag & Drop . Se encuentra en Github en la carpeta ImageGallery_finished .

Dos nuevos delegados proporcionan el rendimiento del mecanismo de Drag & Drop en la Collection View .
Los métodos del primer delegado, dragDelegate , están configurados para inicializar y personalizar los arrastrar y soltar Drags .
Los métodos del segundo delegado, dropDelegate , completan el arrastrar y soltar de Drags y, básicamente, proporcionan Data transfer datos y configuraciones de animación personalizadas cuando Drop reinicia Drop , así como otras cosas similares.

Es importante tener en cuenta que ambos protocolos son completamente independientes. Puede usar uno u otro protocolo si solo necesita "arrastrar" Drag o solo "soltar" Drop , pero puede usar ambos protocolos a la vez y simultáneamente Drag y soltar Drag y "soltar" Drop , lo que abre una funcionalidad adicional Mecanismo de Drag & Drop para reordenar elementos en su Collection View .

Arrastrar y soltar Drag elementos DESDE Collection View


La implementación del protocolo de Drag es muy simple, y lo primero que debe hacer siempre es establecerse, como delegado de dragDelegate :



Y, por supuesto, en la parte superior de la clase ImageGalleryCollectionViewController, debe decir "Sí", implementamos el protocolo UICollectionViewDragDelegate :



Tan pronto como hacemos esto, el compilador comienza a "quejarse", hacemos clic en el círculo rojo y se nos pregunta: "¿Desea agregar los métodos necesarios del protocolo UICollectionViewDragDelegate ?"
Respondo: "¡Por supuesto que quiero!" y haga clic en el botón Fix :



El único método requerido del protocolo UICollectionViewDragDelegate es el método itemsForBeginning , que le dirá al sistema Drag que "arrastramos y soltamos". Se llama al método itemsForBeginning cuando el usuario comienza a "arrastrar" ( Dragging ) una celda en la celda de colección.

Tenga en cuenta que en este método, la Collection View colección ha agregado indexPath . Esto nos dirá qué elemento de la colección, qué indexPath , vamos a "arrastrar y soltar". Esto es realmente muy conveniente para nosotros, ya que es la aplicación la responsable de usar los argumentos de sesión e indexPath para descubrir cómo manejar este arrastrar y soltar de Drag .

Si se devuelve el conjunto [UIDragItems] de los elementos "arrastrables", Drag inicializa el "arrastre" del Drag ; si se devuelve el conjunto vacío [] , Drag ignora el "arrastre" del Drag .

Crearé una pequeña función privada dragItems (en: indexPath) con el argumento indexPath . Devuelve la matriz [UIDragItem] que necesitamos .



¿Cómo es un elemento UIDragItem de arrastrar y soltar?
Él solo tiene una cosa muy IMPORTANTE llamada itemProvider . itemProvider es solo algo que puede proporcionar datos que serán arrastrados.

Y tiene derecho a preguntar: "¿Qué pasa con el" arrastrar y soltar "de un elemento UIDragItem que simplemente no tiene datos?" El elemento que desea arrastrar puede no tener datos, por ejemplo, porque crear estos datos es una operación costosa. Puede ser una imagen de imagen o algo que requiera descargar datos de Internet. Lo bueno es que la operación de Drag & Drop es completamente asíncrona. Cuando comienza a arrastrar y soltar Drag , es realmente un objeto muy liviano ( lift preview ), lo arrastra a todas partes y no sucede nada durante este "arrastre". Pero tan pronto como "suelte" Drop su objeto en algún lugar, entonces, al ser un proveedor de elementos , realmente debe proporcionar su objeto "arrastrado" y "arrojado" con datos reales, incluso si lleva una cierta cantidad de tiempo.

Afortunadamente, hay muchos proveedores de elementos integrados . Estas son clases que ya existen en iOS y que son itemPoviders , como, por ejemplo, NSString , que le permite arrastrar y soltar texto sin fuentes. Por supuesto, esta es una imagen UIImage . Puede seleccionar y arrastrar y soltar imágenes UIImages en todo . La clase NSURL , que es absolutamente maravillosa. Puede ir a la página Web , seleccionar la URL y "soltarla" donde desee. Esto puede ser un enlace a un artículo o una URL para una imagen, como estará en nuestra demostración. Estas son las clases de color de UIColor , elemento de mapa MKMapItem , contacto CNContact de la libreta de direcciones, puede seleccionar y arrastrar muchas cosas. Todos ellos son proveedores de artículos .

Vamos a "arrastrar y soltar" la imagen UIImage . Se encuentra en la celda de la celda Collection View con indexPath , lo que me ayuda a seleccionar la celda de la celda , sacar el Outlet imageView y obtener su imagen .

Expresemos esta idea con un par de líneas de código.
Primero, solicito mi Collection View sobre una celda para el elemento del elemento correspondiente a este indexPath .



El método cellForItem (en: IndexPath) para la Collection View colección funciona solo para las celdas visibles, pero, por supuesto, funcionará en nuestro caso, porque "arrastro y suelto" el elemento de la colección Drag en la pantalla y es visible.

Entonces, obtuve una celda celular "arrastrable".
A continuación, uso el como operador ? a esta celda para que tenga un TIPO de mi subclass personalizada. Y si esto funciona, entonces obtengo un Outlet imageView , del cual tomo su imagen de imagen . Acabo de "capturar" la imagen de esta indexPath .

Ahora que tengo una imagen de imagen , todo lo que tengo que hacer es crear uno de estos UIDragItems usando la imagen de imagen resultante como itemProvider , es decir, lo que nos proporciona los datos.
Puedo crear dragItem usando el constructor UIDragItem , que toma itemProvider como argumento:



Luego creamos un itemProvider para la imagen de la imagen también usando el constructor NSItemProvider . Hay varios constructores para NSItemProvider , pero entre ellos hay uno realmente maravilloso: NSItemProvider (objeto: NSItemProviderWriting) :



Simplemente le da el objeto objeto a este constructor NSItemProvider , y sabe cómo hacer que itemProvider salga de él. Como tal objeto, le doy a la imagen la imagen que recibí de la celda y obtengo itemProvider for UIImage .
Y eso es todo. Creamos dragItem y debería devolverlo como una matriz que tiene un elemento.

Pero antes de que vuelva dragItem , voy a hacer una cosa más, es decir, para establecer la variable localobject para dragItem , igual a la imagen resultante imagen .



¿Qué significa esto?
Si realiza "arrastrar y soltar" Draglocalmente, es decir, dentro de su aplicación, entonces no necesita pasar por todo este código asociado con itemProvider a través de la recuperación de datos asíncrona. No necesita hacer esto, solo necesita tomar localObject y usarlo. Este es un tipo de "cortocircuito" con "arrastrar y soltar" local Drag.

El código escrito por nosotros funcionará al arrastrar y soltarDragfuera de nuestra colección Collection Viewa otras aplicaciones, pero si "arrastramos y soltamos " Draglocalmente, entonces podemos usar localObject . A continuación, devuelvo una matriz de un elemento dragItem .

Por cierto, si por alguna razón no pude obtener una imagen para esta celda de celda , entonces devuelvo una matriz vacía [] , esto significa que Dragse cancela el "arrastrar y soltar" .



Además de objeto local localobject , se puede recordar el contexto local localContext para nuestra Dragsesión de la sesión . En nuestro caso, será una colección Ver coleccióny es útil para nosotros después:



Tener "arrastrar y soltar" Drag, puede agregar más artículos artículos a esta "arrastrar y soltar", sólo haciendo el gesto del grifo en ellos. Como resultado, puede arrastrar y soltar Dragmuchos elementos a la vez. Y esto es fácil de implementar con otro método delegado , UICollectionViewDragDelegate , muy similar al método itemsForeginning , un método llamado itemsForAddingTo . El método itemsForAddingTo se ve exactamente igual que el método itemsForeginning y devuelve exactamente lo mismo, porque también nos da indexPathde lo que el usuario "grabó" durante el proceso de "arrastrar Dragy soltar" , y solo necesito obtener la imagen de la imagen de la celda en la que el usuario "grabó" y devolverla.



Devolver una matriz vacía [] del método itemsForAddingTo hace que el gesto de toque se interprete de la manera habitual, es decir, como seleccionar esta celda de celda .
Y eso es todo lo que necesitamos para arrastrar y soltar Drag.
Lanzamos la aplicación.
Selecciono la imagen de "Venecia", la sostengo por un momento y empiezo a moverme ...



... y realmente podemos arrastrar esta imagen a la aplicaciónPhotos, ya que ve un signo más verde "+" en la esquina superior izquierda de la imagen "arrastrable". Puedo realizar un gesto de toque en una imagen más de Artika de la colección Collection View...



... y ahora podemos colocar dos imágenes en la aplicación Photos:



dado que el Photosmecanismo ya está integrado en la aplicación Drag & Drop, todo funciona bien y es genial.
Entonces, el "arrastrar" Dragy "descargar" la Dropimagen de la Galería en otras aplicaciones me funciona , no tuve que hacer mucho en mi aplicación, excepto entregar la imagen de la imagen como una matriz [UIDragItem] . Esta es una de las muchas características excelentes del mecanismo.Drag & Drop - Es muy fácil hacer que funcione en ambas direcciones.

Restablecer Dropimágenes a la colecciónCollection View


Ahora tenemos que hacer una Dropparte de mi colección Collection Viewpara poder "volcar" Dropcualquier imagen "arrastrada" DENTRO de esta colección. Una imagen "arrastrable" puede "venir" FUERA o directamente DENTRO de esta colección.
Para ello, hacemos lo mismo hecho de delegar dragDelegate , es decir, hacer que nosotros mismos, el auto , delegado dropDelegate en el método de la viewDidLoad :



Una vez más, tenemos que subir a la parte superior de nuestra clase ImageGalleryCollectionViewController y verificar la implementación del protocolo UICollectionViewDropDelegate :



Tan pronto como agregamos nuestro nuevo protocolo, el compilador nuevamente comenzó a "quejarse" de que no implementamos este protocolo. Hacemos clic en el botón Fixy los métodos requeridos de este protocolo aparecen frente a nosotros. En este caso, se nos informa que debemos implementar el método performDrop :



debemos hacer esto, de lo contrario no se producirá un "reinicio" Drop. De hecho, voy a implementar el método performDrop en último lugar, porque hay un par de otros Applemétodos altamente recomendados que necesita implementar para la Dropparte. Esto es canHandle y dropSessionDidUpdate :



Si implementamos estos dos métodos, entonces podemos obtener un pequeño signo más verde "+" cuando arrastramos imágenes desde el EXTERIOR a nuestra colección ollection View, y además, no intentarán volcar lo que no entendemos.

Implementemos canHandle . puede manejar la versión del método , que está destinada para la colección ollection View, pero este método se ollection Viewve exactamente igual que el método similar para UIView normal , no hay indexPath allí , solo necesitamos devolver session.canLoadObjects (ofClass: UIImage.self) , y eso significa que acepto el "reinicio" de los objetos de este cl PAS en mi colección ollection View:



Pero esto no es suficiente para "volcar" la Dropimagen en mi colección Collection ViewEXTERIOR.
Si el "volcado" de la Dropimagen ocurre DENTRO de la colección Collection View, cuando el usuario reorganiza sus propios elementos utilizando el mecanismo Drag & Drop, entonces solo una imagen UIImage es suficiente , y la implementación del método canHandle se verá como la anterior.

Pero si el "volcado" de la Dropimagen ocurre FUERA, entonces solo deberíamos manejar el "arrastrar y soltar" Dragque representa la imagen UIImage junto con URLesta imagen, ya que no vamos a almacenar las imágenes UIImage directamenteen modelo. En este caso, voy a volver cierto en el método canHandle sólo si ambas condiciones llevaron a cabo un par de session.canLoadObjects (ofClass: NSURL.self) && (session.canLoadObjects ofClass: UIImage.self) :



me quedan por determinar si estoy tratando con "reset" EXTERIOR O INTERIOR. Haré esto usando la constante calculada isSelf , para cuyo cálculo puedo usar tal cosa en una Dropsesión de sesión como su Dragsesión local localDragSession . Esta Dragsesión local a su vez tiene un contexto local localContext .
Si recuerdas, establecemos este contexto local en el métodoitemsForVeginning Drag delegado UICollectionViewDragDelegate :



Voy a explorar el contexto local localContext a la igualdad en mi colección CollectionView . Es cierto, TYPE de localContext será Any , y necesito hacer una conversión de TYPE Any usando el operador as. UICollectionView :



si el contexto local (session.localDragSession? .LocalContext as? UICollectionView) es igual a mi colección collectionView , entonces la variable calculada isSelf es verdaderay hay un "reinicio" local DENTRO de mi colección. Si se viola esta igualdad, entonces estamos tratando con un "restablecimiento" DropEXTERIOR.

El método canHandle informa que solo podemos manejar este tipo de "arrastrar y soltar" Dragen nuestra colección Collection View. De lo contrario, más adelante, no tiene sentido hablar de "dumping" Drop.

Si continuamos "reinicio" Drop, sigue siendo hasta el momento en que el usuario va a levantar los dedos fuera de la pantalla y no será un verdadero "reinicio" Drop, tenemos que informar iOSutilizando el método dropSessionDidUpdate delegado UICollectionViewDropDelegate sobre nuestra oferta UIDropProposal para implementar reinicio Drop.

En este método, debemos devolver una Droporación que pueda tener valores .copy o .move o .cancel o .forbidden para el argumento de la operación . Y estas son todas las posibilidades que tenemos en el caso ordinario cuando se trata de una UIView normal .

Pero la colección Collection Viewva más allá y ofrece devolver la oferta especializada UICollectionViewDropProposal , que es una subclassclase de UIDropProposal y permite, además de la operación de operación, especificar un parámetro de intención adicional para la colección Collection View.

Parámetrointent le dice a la colecciónCollection Viewsi queremos que el elemento "descartado" se coloque dentro de una celda existente , o si queremos agregar una nueva celda. ¿ Ves la diferencia? En el caso de la colección,Collection Viewdebemos comunicar nuestra intención intencional .

En nuestro caso, siempre queremos agregar una nueva celda, por lo que verá cuál será nuestro parámetro de intención .
Seleccionamos el segundo constructor para UICollectionViewDropProposal :



en nuestro caso, siempre queremos agregar una nueva celda y el parámetro de intención tomará el valor .insertAtDestinationIndexPath como opuesto.insertIntoDestinationIndexPath .



Una vez más me uso contabilizado constante isself , y si es auto reorganización, I desplazará .move , de lo contrario estoy haciendo hasta .copy . En ambos casos, usamos .insertAtDestinationIndexPath , es decir, insertando nuevas celdas .

Hasta ahora no he implementado el método performDrop , pero echemos un vistazo a lo que una colección ya puede hacerCollection Viewcon esta pequeña información que le proporcionamos.

Arrastro la imagen delSafaribuscadorGoogle, y aparece un signo "+" verde en la parte superior de esta imagen, lo que indica que nuestra Galería de imágenes no solo está lista para aceptar y copiar esta imagen URL, sino que también proporciona un lugar dentro de la colección Collection View:



puedo hacer clic en un par de imágenes Safariy Ya habrá 3 imágenes "arrastradas":



pero si levanto el dedo y "suelto" Dropestas imágenes, no se colocarán en nuestra Galería, sino que simplemente volverán a sus lugares anteriores, porque todavía no hemos implementado el método performDrop .



Se podía ver que la colección Collection Viewya sabe lo que quiero hacer.
La colección Collection Viewes una cosa absolutamente maravillosa para el mecanismo.Drag & Drop, ella tiene una funcionalidad muy poderosa para esto. Apenas la tocamos escribiendo 4 líneas de código, y ella ya se ha movido bastante lejos en la percepción de "reinicio" Drop.
Volvamos al código e implementemos el método performDrop .



En este método, no podremos seguir adelante con 4 líneas de código, porque el método performDrop es un poco más complicado, pero no demasiado.
Cuando se produce el "reinicio" Drop, en el método performDrop necesitamos actualizar nuestro Modelo, que es la galería de imágenes imageGallery con una lista de imágenes , y necesitamos actualizar nuestra colección visual collectionView .

Tenemos dos escenarios diferentes de "reinicio" Drop.

Si hay un "reinicio" Dropde mi colección collectionView , entonces tengo que "reiniciar" el Dropelemento de la colección en un lugar nuevo y eliminarlo del lugar anterior, porque en este caso muevo ( .move ) este elemento de la colección. Esta es una tarea trivial.

Hay un "restablecimiento" Dropdesde otra aplicación, luego debemos usar la propiedad itemProvider del elemento del elemento "arrastrado" para seleccionar datos.

Cuando realizamos un "reinicio" Dropen la colección collectionView , la colección nos proporciona un coordinador coordinador. En primer lugar, nos informó el coordinador del coordinador , que destinationIndexPath , es decir indexPath "-destination", "Reset" Drop, que es donde vamos a ser "reinicio".



Pero destinationIndexPath puede ser nulo , ya que puede arrastrar la imagen "descartada" a esa parte de la colección Collection Viewque no es el lugar entre algunas celdas existentes , por lo que bien podría ser nulo . Si ocurre esta situación, entonces creo un IndexPath con el elemento del elemento 0 en la sección de la sección 0 .



Podría elegir cualquier otro indexPath , pero usaré este indexPath por defecto.

Ahora sabemos dónde realizaremos el "reinicio" Drop. Tenemos que pasar por todos los artículos del coordinador "reiniciables" proporcionados por el coordinador . Cada elemento de esta lista tiene un TIPO UICollectionViewDropItem y puede proporcionarnos datos muy interesantes.

Por ejemplo, si puedo obtener sourceIndexPath de item.sourceIndexPath , entonces sabré con certeza que este "arrastrar y soltar" Dragse realiza por sí mismo, self, y la fuente del arrastre Drages el elemento de la colección con indexPath igual a sourceIndexPath :



ni siquiera tengo que mirar localContext en este caso para descubrir que este "arrastrar y soltar" se realizó DENTRO de la colección collectionView . Wow!

Ahora sé la fuente sourceIndexPath y el "destino" destinationIndexPath Drag & Drop , y la tarea se vuelve trivial. Todo lo que necesito hacer es actualizar el Modelo para que se intercambien el punto de origen y el de destino, y luego actualizar la colección collectionView , en la que deberá eliminar el elemento de la colección con sourceIndexPath y agregarlo a la colección con destinationIndexPath .

Nuestro caso local es el más simple, porque en este caso el mecanismo Drag & Dropfunciona no solo en la misma aplicación, sino en la misma colección collectionView , y puedo obtener toda la información necesaria utilizando el coordinador coordinador. Implementémoslo en este caso local más simple:



en nuestro caso, ni siquiera necesito localObject , que "escondí" antes cuando creé dragItem y que ahora puedo tomar prestado del elemento "arrastrado" en la colección de elementos en el formulario item.localObject . Lo necesitaremos cuando "descarguemos" Dropimágenes en el "bote de basura", que está en la misma aplicación, pero no es la misma colección collectionView . Dos IndexPathes son suficientes para mí ahora : el origen sourceIndexPath y el "destino" destinationIndexPath .

Primero obtengo informaciónimageInfo sobre la imagen en el lugar antiguo del Modelo, eliminándola de allí. Y luego insertar en una serie de imágenes de mis modelos imageGallery información ImageInfo una imagen con un nuevo índice destinationIndexPath.item . Así es como actualicé mi Modelo:



ahora tengo que actualizar la colección collectionView . Es importante entender que no quiero sobrecargar todos los datos de mi colección CollectionView utilizando reloadData () en el medio de "arrastrar y soltar"Drag, ya que restablece todo el "mundo" de nuestra galería de imágenes, que es muy malo, no lo hago. En cambio, voy a ordenar e insertar elementoselementos por separado: eliminé el



elemento de colección collectionView con sourceIndexPath e inserté un nuevo elemento de colección con destinationIndexPath .

Parece que este código funciona muy bien, pero en realidad, este código puede "bloquear" su aplicación. La razón es que realiza numerosos cambios en su colección collectionView , y en este caso cada paso de cambiar la colección debe sincronizarse con el Modelo normalmente, lo que no se observa en nuestro caso, ya que realizamos ambas operaciones al mismo tiempo: eliminar e insertar. De ahí la colección collectionViewen algún momento estará en un estado NO sincronizado con el Modelo.

Pero hay una forma realmente genial de evitar esto, que es que la colección collectionView tiene un método llamado performBatchUpdates que tiene un cierre ( closure) y dentro de este cierre puedo colocar cualquier número de estos deleteItems , insertItems , moveItems y todo lo que quiero:



Ahora, deleteItems e insertItems se realizarán como una sola operación, y nunca habrá una falta de sincronización de su Modelo con la colección collectionView .

Y finalmente, lo último que tenemos que hacer es pedirle al coordinador que implemente y anime el "reinicio" en sí mismo Drop:



tan pronto como levante el dedo de la pantalla, la imagen se mueve, todo sucede al mismo tiempo: "reinicio", la imagen desaparece en un lugar y apariencia en otro.
Intentemos mover la imagen de prueba "Venecia" en nuestra Galería de imágenes al final de la primera línea ...



... y "restablecerla":



como queríamos, se colocó al final de la primera línea.
¡Hurra!¡Todo funciona!

Ahora NO trataremos con el caso local, es decir, cuando el elemento "reset" viene FUERA, es decir, de otra aplicación.
Para hacer esto, escribimos otra cosa en el código con respecto a sourceIndexPath . Si no lo hacemos sourceIndexPath , significa que el elemento de "usar y tirar" vino de fuera en alguna parte y tenemos que utilizar los datos de transferencia usando itemProver volcado "elemento item.dragItem.itemProvider :



Si usted tiene algo que" arrastrar y soltar " Dragdesde el exterior y" tiro "Drop, entonces, ¿esta información está disponible al instante? No, selecciona datos de la cosa "arrastrada" ASINCRÓNICAMENTE. ¿Pero qué pasa si la muestra toma 10 segundos? ¿Qué hará la colección en este momento ollection View? Además, los datos pueden no llegar en el orden en que los solicitamos. Gestionar esto no es fácil, y Applepropone para ollection Vieweste caso una tecnología completamente nueva para el uso de sustitutos Placeholders. Coloca

un Collection Viewmarcador de posición en su colección Placeholder, y la colección lo Collection Viewgestiona todo por usted, por lo que todo lo que tiene que hacer cuando los datos finalmente se seleccionan es pedirle al marcador de posición que Placeholderllame a su marcador de posición Contexto contextualy dile que recibiste la información. Luego actualice su Modelo y contexto placeholderContext Intercambia AUTOMÁTICAMENTE la celda con el marcador de posición Placeholdercon una de sus celdas , que corresponde al tipo de datos que recibió.

Realizamos todas estas acciones creando un contexto de marcador de posición de marcador de posición que gestiona el marcador de posición Placeholdery que obtiene del coordinador coordinador , pidiéndole que "restablezca" Dropel elemento del elemento al marcador de posición Placeholder.

Usaré el inicializador para el contexto de marcador de posición de marcador de posiciónque "arroja" dragItem a un UICollectionViewDropPlaceholder :



El objeto que voy a "lanzar" Dropes item.dragItem , donde item es un elemento for de un bucle, ya que podemos lanzar Dropmuchos elementos coordinator.items . Los "tiramos" uno por uno. Entonces item.dragItem es lo que "arrastramos Dragy soltamos " Drop. El siguiente argumento para esta función es el marcador de posición, y lo crearé usando el inicializador UICollectionViewDropPlaceholder :



Para hacer esto, necesito saber DÓNDE voy a insertar el marcador de posiciónPlaceholder, es decir, insertionIndexPath , así como el identificador de la celda reutilizada reuseIdentifier .
El argumento insertionIndexPath es obviamente igual a destinationIndexPath , es el IndexPath para colocar el objeto "arrastrado", se calcula al comienzo del método performDropWith .

Ahora veamos el id de la celda reuseIdentifier . USTED debe decidir qué tipo de celda celular es su localizador Placeholder. El coordinador coordinador no tiene una celda " preempaquetada " para el localizadorPlaceholder. Es USTED quien debe decidir sobre esta célula celular . Por lo tanto, el identificador de la celda reutilizada reuseIdentifiercell se solicita a los suyos storyboardpara que pueda usarse como un PROTOTIPO.

Lo llamaré "DropPlaceholderCell", pero básicamente, podría nombrarlo como sea.
Esta es solo la cadena String que voy a usar en la mía storyboardpara crear esta cosa.
Regrese a la nuestra storyboardy cree una celda celular para el marcador de posición Placeholder. Para hacer esto, solo necesitamos seleccionar una colección Collection Viewe inspeccionarla. En el primer campo, Itemscambio 1a2. Esto crea inmediatamente una segunda celda para nosotros, que es una copia exacta de la primera.



Seleccionamos nuestra nueva celda ImageCell, establecemos el identificador " DropPlaceholderCell", eliminamos todos los UIelementos desde allí , incluido Image View, ya que este PROTOTIPO se usa cuando la imagen aún no ha llegado. Agregamos un nuevo indicador de actividad de la paleta de objetos allí Activity Indicator, rotará, informando a los usuarios que espero algunos datos de "restablecimiento". También cambiar el color de fondo Backgroundque entender que cuando "Reset" de la imagen, fuera funciona exactamente esta celda celular como prototipos:



Además del tipo de célula nueva no debe ser ImageCollectionVewCell, porque no habrá imágenes en él. Haré que esta celda sea una celda normal de TYPE UIollectionCiewCell , ya que no necesitamos ninguna Outletspara el control:



configuremos el indicador de actividad Activity Indicatorpara que comience a animarse desde el principio, y no tendría que escribir nada en el código para iniciarlo. Para hacer esto, haga clic en la opción Animating:



Y eso es todo. Entonces, hicimos todas las configuraciones para esta celda DropPlaceholderCell, volvemos a nuestro código. Ahora tenemos un gran localizador Placeholderlisto para funcionar.

Todo lo que tenemos que hacer es obtener los datos, y cuando se reciben los datos, simplemente le decimos al marcador de posición Contexto sobre este contexto e intercambiará el marcador de posiciónPlaceholdery nuestra celda "nativa" con datos, y haremos cambios en el Modelo.

Voy a "cargar" UN objeto, que será mi artículo usando el método loadObject (ofClass: UIImage.self) (singular). Yo uso el código item.dragItem.itemProvider proveedor ItemProvider , que proporcionará elementos de datos tienen elemento de forma asíncrona. Está claro que si iitemProvider está conectado , obtenemos el objeto "reset" de iitem fuera de esta aplicación. El siguiente es el método loadObject (ofClass: UIImage.self) (singular):



este cierre particular NO se realiza enmain queue. Y, desafortunadamente, tuvimos que cambiar al main queueuso de DispatchQueue.main.async {} para "capturar" la relación de aspecto de la imagen en la variable de aspecto local Radio .

Realmente presentamos dos variables locales imageURL y aspectRatio ...



... y las "capturaremos" al cargar la imagen de la imagen y la URL URL :



si las variables locales imageURL y aspectoRatio no son nulas , preguntaremos el contexto del marcador de posición de marcador de posición de contexto utilizando el método commitInsertiondénos la oportunidad de cambiar nuestro modelo de imageGallery :



en esta expresión, tenemos insertionIndexPath : este es el indexPath para insertar, y cambiamos nuestro modelo de imageGallery . Eso es todo lo que necesitamos hacer, y este método reemplazará AUTOMÁTICAMENTE un marcador de posición Placeholdercon una celda llamando al método cellForItemAt normal .

Tenga en cuenta que insertionIndexPath puede ser muy diferente de destinationIndexPath . Por quéDebido a que el muestreo de datos puede tomar 10 segundos, por supuesto, es poco probable, pero puede tomar 10 segundos. Durante este tiempo, Collection Viewpueden pasar muchas cosas en la colección . Puede añadir nuevas células de las células , todo ocurre lo suficientemente rápido.

SIEMPRE use insertionIndexPath , y SOLAMENTE insertionIndexPath , para actualizar su Modelo.

¿Cómo actualizamos nuestro modelo?

Insertamos en la matriz imageGallery.images estructura imagemodel , compuesto por la relación de aspecto aspectRatio y la imagen URL imageURL , que nos dio vuelta los correspondientes según el proveedor .

Esto actualiza nuestro modelo de galería de imágenes , y el método commitInsertion hace el resto por nosotros. Ya no necesita hacer nada extra, sin inserciones, sin eliminaciones, nada de esto. Y, por supuesto, dado que estamos en un cierre, necesitamos agregarnos. .



Si estamos por alguna razón no pueda obtener la relación de aspecto aspectRatio y URLla imagen imageURL a partir del correspondiente según el proveedor , se podría haber recibido un error de error en lugar de por el proveedor , tenemos que hacerles saber el contexto placeholderContext , es necesario destruir este un marcador de posición Placeholder, porque todos somos lo mismo, no podemos obtener otros datos:



una cosa a tener en cuenta es URLsque provienen de lugares como este Google; en realidad, necesitan transformaciones menores para "limpiarse"URLpara la imagen Se puede ver cómo se resuelve este problema en esta aplicación de demostración en un archivo Utilities.swiften Github .
Por lo tanto, cuando recibimos una URLimagen, usamos la propiedad imageURL de la clase URL :



y eso es todo lo que necesitas hacer para aceptar FUERA de algo dentro de la colección Collection View.

Vamos a verlo en acción. Lanzamos simultáneamente en modo multitarea nuestra aplicación de demostración ImageGalleryy Safaricon un motor de búsqueda Google. En Googlebuscamos imágenes sobre el tema de "Dawn" (amanecer). En Safariya construidoDrag & Dropel mecanismo, para que podamos seleccionar una de estas imágenes, mantenerla presionada durante mucho tiempo, moverla un poco y arrastrarla a nuestra Galería de imágenes.



La presencia de un signo más verde "+" indica que nuestra aplicación está lista para aceptar una imagen de terceros y copiarla en su colección en la ubicación especificada por el usuario. Después de "restablecerlo", lleva un tiempo descargar la imagen, y en este momento funciona Placeholder: una



vez completada la descarga, la imagen "restablecer" se coloca en el lugar correcto y Placeholderdesaparece:



podemos continuar "restableciendo" las imágenes y colocarlas en nuestro colecciones de aún más imágenes:



después del trabajo de "reinicio" Placeholder:



Como resultado, nuestra Galería de imágenes se llena de nuevas imágenes:



Ahora que está claro que somos capaces de tomar fotografías desde el exterior, no necesitamos la imagen de prueba, y quitaremos:



Nuestro viewDidLoad se vuelve muy simple: es que estamos haciendo nuestra Controller Dragy Dropdelegar y añadimos reconocedor gesto de pellizco , que regula el número de imágenes por línea:



Por supuesto , podemos agregar un caché para las imágenes de imageCache :



rellenaremos el imageCache cuando se "restablezca" Dropen el método performDrop ...



y cuando se recupere de la "red" en la clase personalizada ImageCollectionViewCell :



y usaremos el caché de imageCache al reproducir una celdacelda de nuestra Galería de imágenes en la clase personalizada ImageCollectionViewCell :



Ahora comenzamos con una colección vacía ...



... luego "soltamos" una nueva imagen en nuestra colección ...



... la imagen se carga yPlaceholderfunciona ...



... y la imagen aparece en el lugar correcto:



Continuamos llenando nuestra colección FUERA:



Viene cargar imágenesPlaceholdersfunciona ...



Y las imágenes aparecen en el lugar correcto: por lo



tanto, podemos hacer mucho con nuestra Galería de imágenes: llenarla EXTERIOR, reorganizar elementos INTERIORES, compartir imágenes con otras aplicaciones niyami
Solo tenemos que enseñarle cómo deshacerse de las imágenes innecesarias al "restablecerlas"Dropen la "papelera" presentada en la barra de navegación a la derecha. Como se describe en la sección "Características de la aplicación de demostración de la galería de imágenes", el "bote de basura" está representado por la clase GabageView , que hereda de UIView y debemos enseñarle a aceptar imágenes de nuestra colección ollection View.

Restablecer Dropimágenes de la Galería a la papelera.


Inmediatamente del lugar - a la cantera. Voy a añadir a GabageView "interacción" Interacción y será UIDropInteraction , ya que estoy tratando de "reset" Dropalgunas cosas. Todo lo que necesitamos para proporcionar esta UIDropInteraction es un delegado delegado , y me voy a asignar yo mismo a este delegado delegado :



Naturalmente, nuestra clase GabageView debe confirmar que estamos implementando el protocolo UIDropInteractionDelegate :



todo lo que debemos hacer para que funcione Drop, esto es para implementar los métodos canHandle que ya conocemos ,sessionDidUpdate y performDrop .



Sin embargo, a diferencia de métodos similares para la recolecciónCollection View, no tenemos ninguna información adicional en forma de un IndexPath del lugar de descarga.

Implementemos estos métodos.
Dentro del método canHandle será procesada sólo el "arrastrar y soltar"Drag, que representan la imagen un UIImage . Por lo tanto,solodevolveré verdadero si session.canLoadObjects (ofClass: UIImage.self) :



en el método canHandle ,esencialmente solo dice que si el objeto "arrastrable" no es una imagen UIImage, entonces, además, no tiene sentido continuar la caída de "restablecimiento" y llamar a los métodos posteriores.
Si el objeto "arrastrable" es una imagen UIImage , ejecutaremos el método sessionDidUpdate . Todo lo que necesitamos hacer en este método es devolver nuestra oferta de "reinicio" de UIDropProposalDrop . Y estoy listo para aceptar solo el objeto "arrastrado" LOCALMENTE UIImage TYPE que se puede "soltar" en Dropcualquier lugar dentro de mi GarbageView . Mi GarbageView no interactuará con imágenes volcadas FUERA. Así que estoy analizando usando la variable session.localDragSessionsi hay un "reinicio" local Drop, y devuelvo la oración "reinicio" en forma del constructor UIDropProposal con el argumento de la operación tomando el valor .copy , porque SIEMPRE LOCAL "arrastrar y soltar" Dragen mi aplicación vendrá de la colección Collection View. Si "arrastrar Dragy soltar" y "restablecer" Dropocurren FUERA, entonces devuelvo la frase "restablecer" en forma del constructor UIDropProposal con el argumento de operación tomando el valor .fobbiden , es decir, "prohibido" y obtendremos un signo de prohibición "restablecer" en lugar del signo más verde .



Copiar una imagen UIImage, simularemos una disminución en su escala a casi 0, y cuando ocurra el "reinicio", eliminaremos esta imagen de la colección Collection View.
Para crear el usuario la ilusión de "reinicio, y la desaparición de" imagen en el "cubo de basura", se utiliza un nuevo método para nosotros previewForDropping , que le permite redirigir el "reinicio" Dropen otro lugar y al mismo tiempo transformar el objeto "desechable" durante la animación:



En En este método, usando el inicializador UIDragPreviewTarget, obtenemos una nueva vista previa para que el objeto de destino se descarte y lo redirigimos usando el método retargetedPreviewa un nuevo lugar, al "bote de basura", con su escala reducida a casi cero:



si el usuario levanta un dedo hacia arriba, se produce un "reinicio" Drop, y yo (como GarbageView ) recibo un mensaje performDrop . En el mensaje performDrop, realizamos el "reset" real Drop. Honestamente, la imagen que se volcó en GarbageView ya no nos interesa, ya que la haremos prácticamente invisible, lo más probable es que el hecho mismo de completar el "reinicio" Dropindique que eliminamos esta imagen de la colección Collection View. Para lograr esto, debemos conocer la colección en sí y la colección indexPathimagen descartada en ella. ¿De dónde podemos conseguirlos?

Debido a que el proceso Drag & Dropse lleva a cabo en una sola aplicación, que está disponible para todos nosotros los locales: local de Dragsesión localDragSession nuestra Dropsesión de la sesión , el contexto local localContext , que es nuestra colección de sollectionView y objeto local localobject , lo que podemos hacer por sí mismo se restablece la imagen imagen de la "Galería" o indexPath . Debido a esto podemos obtener en el método performDrop clase GarbageView colección colección , y su usodataSource cómo ImageGalleryCollectionViewController y modelo imageGallery nuestraController, podemos obtener una serie de imágenes de imágenes TIPO [ImageModel]:



Con la ayuda del local deDragla sesión localDragSession nuestraDropsesión de la sesión hemos sido capaces de conseguir todo el "arrastre" en la GarbageView Drag elementos de artículos , y puede haber una gran cantidad, tal como la conocemos, y Todos ellos son imágenes de nuestra colección collectionView . Creación deDragelementos de dragItems nuestra colecciónCollection View, hemos proporcionado para cada "arrastre"DragelementodragItem objeto local localobject , que es la imagen de la imagen , pero es que no vienen muy bien durante la recolección de reorganización interna CollectionView , pero el "reset" Galería de imágenes "bote de basura" que necesitan desesperadamente en la instalación local de localobject "arrastrar" objeto dragItem , después de todo este tiempo no tenemos un coordinador , que generosamente comparte información sobre lo que está sucediendo en la colección collectionView . Por lo tanto, queremos que el objeto local localobject índice fue indexPath en la matriz de la imagen imágenes de nuestros modelosimageGallery . Hacer los cambios necesarios en el método dragItems (en indexPath: IndexPath) clase ImageGalleryCollectionViewController :



Ahora que podemos tomar cada "pretaskivaemogo" elemento de elemento que localobject , que es el índice indexPath en la matriz de la imagen imágenes de nuestros modelos galería de imágenes , y enviarlo a los índices de matriz índices y matriz de indexPahes de imágenes eliminadas:



Conocer la matriz de índices índices y la matriz de indexPahes de imágenes eliminadas, en el método performBatchUpdatescolección colección que eliminar todas las imágenes borradas de los modelos de las imágenes y de la colección de la colección :



Ejecutar la aplicación, llenar la galería con nuevas imágenes:



Seleccione un par de imágenes que queremos eliminar de nuestra galería ...



... "tirar" de ellos en el icono con el "cubo de basura" ...



Se redujo casi a 0 ...



... y desaparecer de la colección Collection View, escondiéndose en el "bote de basura":



Guardar imágenes entre inicios.



Para guardar la Galería de imágenes entre ejecuciones, usaremos UserDefaults , después de convertir nuestro Modelo a un JSONformato. Para ello, añadiremos a nuestra Controllervariable de defailts var ...



..., y en el modelo ImageGallery y ImageModel protocolo codificable :



Cadena de cuerda , las matrices de la matriz , la URL y doble ya están implementando el protocolo codificable , por lo que no tenemos otra cosa que hacer para llegar a la codificación de trabajo y decodificar en modelos ImageGallery en JSONformato.
¿Cómo obtenemos la JSONversión de ImageGallery ?
Para crear este ampliado variable de JSON var , que devuelve el resultado de los intentos de convertir en sí, , a través de JSONEncoder.encode () en el JSONformato:



Y eso es todo. Cualquiera de los Datos se devolverá como resultado de la conversión de uno mismo al formato JSON, o nulo si esta conversión falla, aunque esto último nunca ocurre, porque este TIPO es 100% Codificable . La variable opcional json se usa solo por razones de simetría.
Ahora tenemos una manera de convertir los modelos de ImageGallery a formato de datosJSON . ¿La variable json tiene un tipo de datos? que se puede recordar en UserDefaults .
Ahora imagine que de alguna manera logramos obtener los JSONdatos json , y me gustaría recrear a partir de ellos nuestro Modelo, una instancia de la estructura de ImageGallery . Para hacer esto, es muy fácil escribir un INICIALIZADOR para ImageGallery , cuyo argumento de entrada son JSONdatos json . Este inicializador será un inicializador "en caída"failable) Si no se puede inicializar, se bloquea y devuelve nil :



solo obtengo el valor newValue usando el decodificador JSONDecoder , tratando de decodificar los datos json que se pasan a mi inicializador, y luego lo asigno yo mismo .
Si logré hacer esto, entonces obtengo una nueva instancia de ImageGallery , pero si mi intento falla, entonces devuelvo nada , ya que mi inicialización "falló".
Debo decir que aquí tenemos muchas más razones para "fallar" ( fail), porque es muy posible que los JSONdatos jsonpuede estropearse o vaciarse, todo esto puede conducir a la "caída" ( fail) del inicializador.

Ahora podemos aplicar la LEA JSONdatos y modelo de recuperación ImageGallery el método viewWillAppear nuestra Controller...



... así como una entrada en el observador didSet {} propiedades galería de imágenes :



Vamos a ejecutar la aplicación y llenar nuestra galería de imágenes:



Si cerramos la aplicación y abrirlo de nuevo, podemos ver nuestra galería anterior Imágenes guardadas en UserDefaults .

Conclusión


Este artículo demuestra lo fácil que es integrar tecnología Drag & Dropen una iOSaplicación utilizando el ejemplo de una aplicación de demostración muy simple "Galería de imágenes" . Esto hizo posible editar completamente la Galería de imágenes, "arrojando" nuevas imágenes de otras aplicaciones allí, moviendo las existentes y eliminando las innecesarias. Y también para distribuir imágenes acumuladas en la Galería a otras aplicaciones.

Por supuesto, nos gustaría crear muchas de estas pintorescas colecciones temáticas de imágenes y guardarlas directamente en el iPad o iCloud Drive. Esto se puede hacer si cada galería se interpreta como un documento UID almacenado permanentemente. Tal interpretación nos permitirá elevarnos al siguiente nivel de abstracción y crear una aplicación que funcione con documentos. En tal aplicación, sus documentos serán mostrados por el componente DocumentBrowserViewController , muy similar a la aplicación Files. Le permitirá crear imágenes de UIDocument del tipo "Galería de imágenes" tanto en su iPadcomo en él iCloud Drive, así como seleccionar el documento deseado para ver y editar.
Pero este es el tema del próximo artículo.

PD El código de la aplicación de demostración antes de la implementación del mecanismo Drag & Dropy después está en Github .


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


All Articles