iOS 11 ha elevado iOS, en particular para el iPad, a una verdadera plataforma multitarea, gracias a Drag and Drop. Esto promete borrar los límites entre las aplicaciones, permitiendo que el contenido se comparta fácilmente. Aprovechando el toque múltiple, iOS 11 permite que el contenido se mueva de una manera natural e intuitiva, acercando los dispositivos móviles de Apple a la paridad con la riqueza que disfrutan los usuarios de computadoras de escritorio y portátiles..
Esta característica tan esperada le permite arrastrar elementos a otra ubicación en la misma aplicación oa otra aplicación. Esto funciona a través de un arreglo de pantalla dividida o a través del dock, usando un gesto continuo. Además, los usuarios no se limitan a seleccionar solo elementos, sino que pueden arrastrar varios elementos al mismo tiempo. Muchas aplicaciones, incluidas las aplicaciones del sistema como Fotos y Archivos, aprovechan la selección múltiple y el arrastre de múltiples archivos.
Este tutorial te enseñará a arrastrar y soltar, y luego profundizarás en la arquitectura y estrategia de usar el nuevo SDK de arrastrar y soltar en una aplicación basada en vista de tabla. Quiero ayudar a los desarrolladores, como usted, a adaptar sus aplicaciones al comportamiento de la IU emergente que se convertirá en estándar en futuras aplicaciones de iOS.
En este tutorial, cubriremos lo siguiente:
En la segunda mitad de este tutorial, realizaremos los pasos prácticos para habilitar una aplicación de vista de tabla simple para aprovechar el arrastrar y soltar, comenzando con una de las plantillas predeterminadas de vista de tabla de Apple que está disponible cuando crea un nuevo proyecto en Xcode. 9. Continúa y clona el repositorio GitHub del tutorial si quieres seguirlo..
Este tutorial asume que tiene experiencia como desarrollador de iOS y que ha usado las bibliotecas UIKit en Swift o Objective-C, incluyendo UITableView
, y que tengas cierta familiaridad con delegados y protocolos..
Usando la nomenclatura de Apple, un elemento visual se arrastra desde la ubicación de origen y se coloca en la ubicación de destino. Esto se denomina actividad de arrastre, ya que la actividad se lleva a cabo dentro de una sola aplicación (se admiten iPad y iPhone) o en dos aplicaciones (solo disponibles en iPad).
Mientras una sesión de arrastre está en progreso, las aplicaciones de origen y destino aún están activas y se ejecutan normalmente, lo que respalda las interacciones del usuario. De hecho, a diferencia de macOS, iOS admite múltiples actividades de arrastre simultáneas al usar múltiples dedos.
Pero concentrémonos en un solo elemento de arrastre y en cómo utiliza una promesa como un contrato para sus representaciones de datos.
Cada elemento de arrastre se puede considerar como una promesa, una representación de datos contenidos que se arrastrará y soltará desde una fuente hasta su destino. El elemento de arrastre utiliza un proveedor de elementos, llenando su registeredTypeIdentifiers
con identificadores de tipo uniformes, que son representaciones de datos individuales que se comprometerán a entregar a su destino previsto junto con una imagen de vista previa (que se ancla visualmente debajo del punto de contacto del usuario), como se ilustra a continuación:
El elemento de arrastre se construye a través de la UIDragInteractionDelegate
desde la ubicación de origen y manejado en la ubicación de destino a través de la UIDropInteractionDelegate
. La ubicación de origen debe ajustarse a la NSItemProviderWriting
protocolo, y la ubicación de destino debe cumplir con la NSItemProviderReading
protocolo.
Esa es una descripción básica de nominar una vista como un elemento de arrastre, a través de promesas. Veamos cómo implementamos un origen de arrastre desde una vista, antes de establecer el destino de soltar.
Al centrar nuestra atención en la primera parte del arrastrar y soltar (la fuente de arrastre), debemos realizar los siguientes pasos cuando el usuario inicia una actividad de arrastre:
UIDragInterationDelegate
protocolo.dragInteraction (_: itemsForBeginning :)
. Lo primero que deberá hacer es conformar su vista nominada a la UIDragInterationDelegate
protocolo, creando un nuevo UIDragInteraction
instancia y asociándolo con su ViewController
la vista de addInteraction
propiedad, así como su delegado, de la siguiente manera:
deje que dragInteraction = UIDragInteraction (delegate: dragInteractionDelegate) view.addInteraction (dragInteraction)
Después de declarar su origen de arrastre, procederá a crear un elemento de arrastre, esencialmente una promesa de representación de datos, implementando el método de delegado dragInteraction (_: itemsForBeginning :)
, que el sistema llama para devolver una matriz de uno o más elementos de arrastre para rellenar la propiedad de elementos de la sesión de arrastre. El siguiente ejemplo crea un NSItemProvider
de una promesa de imagen, antes de devolver una matriz de elementos de datos:
func dragInteraction (_ interacción: UIDragInteraction, itemsForBeginning session: UIDragSession) -> [UIDragItem] guard let imagePromise = imageView.image else return [] // Al devolver una matriz vacía, desactiva el arrastre. let provider = NSItemProvider (objeto: imagePromise) let item = UIDragItem (itemProvider: provider) devolver [item]
El método de delegado anterior responde a una solicitud de arrastre que se activa cuando el usuario comienza a arrastrar el elemento, con el Reconocedor de gestos (UIGestureRecognizer
) enviando un mensaje de "arrastre iniciado" al sistema. Esto es lo que esencialmente inicializa la "sesión de arrastre".
A continuación, procedemos a implementar el destino de soltar, para manejar la matriz de elementos de arrastre iniciados en la sesión.
Del mismo modo, para conformar su vista designada para aceptar y consumir datos como parte del destino de descarte, deberá completar los siguientes pasos:
DropInteraction
.dropInteraction (_: canHandle :)
.dropInteraction (_: sessionDidUpdate :)
Método de protocolo, que indica si va a copiar, mover, rechazar o cancelar la sesión.dropInteraction (_: performDrop :)
método de protocolo.Al igual que configuramos nuestra vista para habilitar el arrastre, configuraremos simétricamente nuestra vista nominada para aceptar los elementos soltados de una sesión de arrastre, usando la UIDropinteractionDelegate
e implementando su DropInteraction
método de delegado:
let dropInteraction = UIDropInteraction (delegate: dropInteractionDelegate) view.addInteraction (dropInteraction)
Para designar si una Vista es capaz de aceptar elementos de arrastre o se niega, implementamos el dropInteraction (_: canHandle :)
método de protocolo. El siguiente método le permite a nuestra vista decirle al sistema si puede aceptar los elementos, indicando el tipo de objetos que puede recibir, en este caso UIImages.
func dropInteraction (_ interaccion: UIDropInteraction, canHandle session: UIDropSession) -> Bool // Indique explícitamente el tipo de elemento de drop aceptable aqui return session.canLoadObjects (ofClass: UIImage.self)
Si el objeto de vista no acepta ninguna interacción de eliminación, debe devolver falso desde este método.
A continuación, enlace una propuesta de soltar para aceptar datos de la sesión de soltar. Aunque este es un método opcional, se recomienda encarecidamente que implemente este método, ya que proporciona indicaciones visuales sobre si la caída dará lugar a la copia del elemento, al moverlo o si se rechazará la eliminación por completo. Al implementar el dropInteraction (_: sessionDidUpdate :)
método de protocolo, que devuelve un UIDropProposal
, indica el tipo de propuesta utilizando el tipo de enumeración de la operación específica (UIDropOperation
). Los tipos válidos que podrías devolver son:
cancelar
prohibido
dupdo
movimiento
func dropInteraction (_ interaccion: UIDropInteraction, sessionDidUpdate session: UIDropSession) -> UIDropProposal // Indique al sistema que moverá el elemento desde la aplicación de origen (también podría indicar .copy para copiar en lugar de mover). operación: .move)
Y, finalmente, para consumir el contenido del elemento de datos dentro de su ubicación de destino, debe implementar el dropInteraction (_: performDrop :)
Método de protocolo en la cola de fondo (en lugar de en la cola principal, esto garantiza la capacidad de respuesta). Esto se ilustra a continuación:
func dropInteraction (_ interaccion: UIDropInteraction, performDrop session: UIDropSession) // Consume UIImage arrastrando elementos session.loadObjects (ofClass: UIImage.self) items en let images = items as! [UIImage] self.imageView.image = images.first
Hemos demostrado cómo implementaría arrastrar y soltar en una vista personalizada, por lo que ahora vamos a pasar a la parte práctica de este tutorial e implementar arrastrar y soltar en una aplicación con una vista de tabla..
Hasta ahora, hemos estado discutiendo cómo implementar arrastrar y soltar en vistas personalizadas, pero Apple también ha facilitado el aumento de las vistas de tabla y las vistas de colección con solo arrastrar y soltar. Mientras que los campos de texto y las vistas admiten automáticamente arrastrar y soltar fuera del cuadro, las vistas de tabla y colección exponen métodos, delegados y propiedades específicos para personalizar sus comportamientos de arrastrar y soltar. Vamos a echar un vistazo a esto en breve..
Comience creando un nuevo proyecto en Xcode 9, asegurándose de seleccionar Aplicación Master-Detail desde la ventana de la plantilla:
Antes de comenzar a trabajar en el resto de los pasos, siga adelante, genere y ejecute el proyecto y juegue un poco con él. Verá que genera una nueva fecha de marca de tiempo cuando selecciona el signo más (+) botón Vamos a mejorar esta aplicación permitiendo al usuario arrastrar y ordenar las marcas de tiempo.
El arrastrar y soltar se admite en las vistas de tabla (así como en las colecciones) a través de API especializadas que permiten arrastrar y soltar con filas, al conformar nuestra vista de tabla para adoptar tanto UITableViewDragDelegate
y UITableViewDropDelegate
protocolos Abre el MasterViewController.swift archivo y añadir lo siguiente a la viewDidLoad ()
método:
anular func viewDidLoad () super.viewDidLoad () ... self.tableView.dragDelegate = self self.tableView.dropDelegate = self ...
Como hicimos con las vistas personalizadas, necesitamos manejar la nueva sesión de arrastre cuando el usuario arrastra una fila seleccionada o varias filas / selecciones. Lo hacemos con el método delegado. tableView (_: itemsForBeginning: at :)
. Dentro de este método, puede devolver una matriz completada que comienza a arrastrar las filas seleccionadas, o una matriz vacía para evitar que el usuario arrastre contenido de esa ruta de índice específica.
Agregue el siguiente método a su MasterViewController.swift expediente:
func tableView (_ tableView: UITableView, itemsForBeginning session: UIDragSession, en indexPath: IndexPath) -> [UIDragItem] let dateItem = self.objects [indexPath.row] como! String let data = dateItem.data (usando: .utf8) let itemProvider = NSItemProvider () itemProvider.registerDataRepresentation (forTypeIdentifier: kUTTypePlainText como String, visibilidad: .todos) completado (datos, nil) return nil return itemProvider: itemProvider)]
Parte del código agregado ya debe ser familiar para usted de la sección anterior, pero básicamente lo que estamos haciendo es crear un elemento de datos del objeto seleccionado, envolverlo en un NSItemProvider
, y devolverlo en un DragItem
.
Dirigiendo nuestra atención al lado de habilitar la sesión abierta, continúe agregando los siguientes dos métodos:
func tableView (_ tableView: UITableView, canHandle session: UIDropSession) -> Bool return session.canLoadObjects (ofClass: NSString.self) func tableView (_ tableView: UITableView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath) UITableViewDropProposal if tableView.hasActiveDrag if session.items.count> 1 return UITableViewDropProposal (operation: .cancel) else return UITableViewDropProposal (propiedad:) inserción de un objeto). copia, intento: .insertAtDestinationIndexPath)
El primer método le dice al sistema que puede manejar tipos de datos de String como parte de su sesión drop. El segundo método de delegado., tableView (_: dropSessionDidUpdate: withDestinationIndexPath :)
, rastrea la ubicación potencial de la gota, notificando el método con cada cambio. También muestra información visual para que el usuario sepa si una ubicación específica está prohibida o es aceptable, utilizando un pequeño icono visual..
Por último, manejamos la caída y consumimos el elemento de datos, llamando tableView (_: performDropWith :)
, recuperando la fila del elemento de datos arrastrados, actualizando el origen de datos de nuestra vista de tabla e insertando las filas necesarias en la tabla.
func tableView (_ tableView: UITableView, performDropWith coordinator: UITableViewDropCoordinator) let destinationIndexPath: IndexPath if let indexPath = coordinator.destinationIndexPath destinationIndexPath = indexPath else // Obtiene la última ruta de índice de la vista de tabla. let section = tableView.numberOfSections - 1 let row = tableView.numberOfRows (inSection: section) destinationIndexPath = IndexPath (row: row, section: section) coordinator.session.loadObjects (ofClass: NSString.self) items in // Consume arrastrar elementos. deja stringItems = items as! [String] var indexPaths = [IndexPath] () for (index, item) en stringItems.enumerated () let indexPath = IndexPath (row: destinationIndexPath.row + index, sección: destinationIndexPath.section) self.objects.insert (item , en: indexPath.row) indexPaths.append (indexPath) tableView.insertRows (en: indexPaths, con: .automatic)
Para obtener más información sobre la compatibilidad con la función de arrastrar y soltar en sus vistas de tabla, consulte la propia documentación del desarrollador de Apple sobre la compatibilidad con la función de arrastrar y soltar en vistas de tabla..
El contenido que cubrimos debe ayudarlo a implementar la función de arrastrar y soltar en sus aplicaciones, permitiendo a los usuarios moverse de forma visual e interactiva por el contenido dentro de sus aplicaciones existentes, así como entre aplicaciones..
Sin embargo, junto con el conocimiento técnico de cómo implementar el arrastrar y soltar, es imperativo que se tome el tiempo para considerar cómo implementaría el arrastrar y soltar, siguiendo las mejores prácticas defendidas por Apple en sus Pautas de Interfaz Humana (HIG), en Para proporcionar a los usuarios la mejor experiencia de usuario posible para iOS 11.
Para concluir, trataremos algunos de los aspectos más importantes a considerar, comenzando con señales visuales. De acuerdo con el HIG, la experiencia fundamental con la función de arrastrar y soltar es que cuando un usuario interactúa con algún contenido, las señales visuales indican al usuario una sesión de arrastre activa, denotada por el aumento del elemento de contenido, junto con una insignia para indicar cuándo se realiza la caída. o no es posible.
Ya hemos utilizado esta mejor práctica en nuestros ejemplos anteriores, cuando incluimos la tableView (_: dropSessionDidUpdate: withDestinationIndexPath :)
Método, que indica si el destino de la gota es un movimiento, una copia o está prohibido. Con las vistas e interacciones personalizadas, debe asegurarse de mantener el conjunto esperado de comportamientos que admiten otras aplicaciones de iOS 11, especialmente las aplicaciones de sistema..
Otro aspecto importante a considerar es decidir si su sesión de arrastre resultará en un movimiento o copia. Como regla general, Apple sugiere que cuando trabaje dentro de la misma aplicación, en general debería resultar en un movimiento, mientras que tiene más sentido copiar el elemento de datos cuando arrastra entre diferentes aplicaciones. Si bien hay excepciones, por supuesto, el principio subyacente es que debe tener sentido para el usuario, y lo que esperan que suceda.
También debe pensar en términos de fuentes y destinos, y si tiene sentido arrastrar algo o no.
Echemos un vistazo a algunas de las utilidades del sistema de Apple. Las notas, por ejemplo, le permiten seleccionar y arrastrar contenido de texto a otras ubicaciones dentro de la aplicación o a otras aplicaciones en el iPad, a través de pantalla dividida. La aplicación Recordatorios le permite mover elementos de recordatorio de una lista a otra. Piense en términos de funcionalidad cuando decida cómo los usuarios usan su contenido.
La guía de Apple es que todo el contenido editable debería admitir la aceptación de contenido eliminado, y cualquier contenido que se pueda seleccionar debe aceptar contenido que se pueda arrastrar, además de admitir copiar y pegar para esos tipos de elementos. Al aprovechar las vistas de texto del sistema estándar y los campos de texto, obtendrá soporte para arrastrar y soltar fuera del cuadro.
También debe admitir la función de arrastrar y soltar elementos múltiples, en lugar de solo admitir elementos individuales, por lo que los usuarios pueden usar más de un dedo para seleccionar varios elementos al mismo tiempo, apilando los elementos seleccionados en un grupo para colocarlos en sus destinos previstos. Un ejemplo de esto en acción es seleccionar varias imágenes en la aplicación Fotos o varios archivos en la aplicación Archivos.
Una guía final es proporcionar a los usuarios la capacidad de revertir una acción o "deshacer" una gota. Los usuarios se han acostumbrado durante mucho tiempo al concepto de deshacer una acción en la mayoría de las aplicaciones populares, y arrastrar y soltar no debería ser una excepción. Los usuarios deben tener la confianza de poder iniciar un arrastrar y soltar y poder revertir esa acción si sueltan el elemento en el destino incorrecto.
Hay muchas más pautas sobre prácticas recomendadas de arrastrar y soltar más allá de lo que hemos visto, incluida la forma de admitir señales de indicadores visuales caídas, mostrar acciones caídas fallidas e indicadores de progreso para sesiones de arrastre no instantáneas, como transferencias de datos. Consulte las pautas de la interfaz humana de iOS de Apple sobre arrastrar y soltar, para obtener una lista completa de las mejores prácticas.
En este tutorial, aprendió cómo enriquecer sus aplicaciones de iOS con arrastrar y soltar, cortesía de iOS 11. En el camino, exploramos cómo habilitar tanto las vistas personalizadas como las vistas de tabla como fuentes de arrastre y destinos de arrastre.
Como parte de la evolución de iOS hacia una interfaz de usuario más manejada por gestos, no hay duda de que arrastrar y soltar se convertirá rápidamente en una característica esperada para los usuarios de todo el sistema, y como tal, todas las aplicaciones de terceros también deben cumplir. Y tan importante como implementar la función de arrastrar y soltar, deberá implementarlo correctamente, de modo que se convierta en una segunda naturaleza para los usuarios, abarcando la simplicidad y la funcionalidad..
Y mientras esté aquí, eche un vistazo a algunas de nuestras otras publicaciones sobre el desarrollo de aplicaciones de iOS!