
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:
- primero, dotaremos a nuestra Collection Viewcapacidad de "arrastrar"DragDE ella UIImage imágenes tanto externa como localmente,
- luego le enseñaremos a nuestra colección de imágenes de Collection ViewdeCollection Viewpara aceptar "arrastrar"Dragexterna o localmente desde UIImage ,
- 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 Viewlocal y eliminarlas de laCollection 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"
¿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
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"
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
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ón
... 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
... y ahora podemos colocar dos imágenes en la aplicación Photos: dado que el
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 :
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
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"
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
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
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
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 :
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"
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 .
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 hacer
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
puedo hacer clic en un par de imágenes Safariy Ya habrá 3 imágenes "arrastradas": pero si levanto el dedo y "suelto"
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
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"
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
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"
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 .
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"
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"
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 (
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
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 ...
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":
... y "restablecerla": como queríamos, se colocó al final de la primera línea.
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 "
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. Colocaun 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"
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ón
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
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
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
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
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 en
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 :
... 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 :
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
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
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
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
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
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
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:
podemos continuar "restableciendo" las imágenes y colocarlas en nuestro colecciones de aún más imágenes: después del trabajo de "reinicio"
después del trabajo de "reinicio" Placeholder: Como resultado, nuestra Galería de imágenes se llena de nuevas imágenes:
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:
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
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 :
Por supuesto , podemos agregar un caché para las imágenes de imageCache : rellenaremos el imageCache cuando se "restablezca"
rellenaremos el imageCache cuando se "restablezca" Dropen el método performDrop ... y cuando se recupere de la "red" en la clase personalizada ImageCollectionViewCell :
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 :
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 ...
Ahora comenzamos con una colección vacía ... ... luego "soltamos" una nueva imagen en nuestra colección ...
... luego "soltamos" una nueva imagen en nuestra colección ... ... la imagen se carga y
... la imagen se carga yPlaceholderfunciona ... ... y la imagen aparece en el lugar correcto:
... y la imagen aparece en el lugar correcto: Continuamos llenando nuestra colección FUERA:
Continuamos llenando nuestra colección FUERA: Viene cargar imágenes
Viene cargar imágenesPlaceholdersfunciona ... Y las imágenes aparecen en el lugar correcto: por lo
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 niyamiSolo tenemos que enseñarle cómo deshacerse de las imágenes innecesarias al "restablecerlas"
tanto, podemos hacer mucho con nuestra Galería de imágenes: llenarla EXTERIOR, reorganizar elementos INTERIORES, compartir imágenes con otras aplicaciones niyamiSolo 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 :
Naturalmente, nuestra clase GabageView debe confirmar que estamos implementando el protocolo UIDropInteractionDelegate : todo lo que debemos hacer para que funcione
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ón
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 UIDropProposal
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
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:
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"
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 de
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:
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 :
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:
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 ...
Seleccione un par de imágenes que queremos eliminar de nuestra galería ... ... "tirar" de ellos en el icono con el "cubo de basura" ...
... "tirar" de ellos en el icono con el "cubo de basura" ... Se redujo casi a 0 ...
Se redujo casi a 0 ... ... y desaparecer de la colección
... 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 :
..., 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
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í, 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
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" (
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 :
... 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:
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 .
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 .