Ziehen Sie Ihre iOS-Apps per Drag & Drop



Der Drag & Drop Mechanismus unter iOS 11 und iOS 12 ist eine Möglichkeit, Daten sowohl innerhalb einer einzelnen Anwendung als auch zwischen verschiedenen Anwendungen grafisch asynchron zu kopieren oder zu verschieben. Obwohl diese Technologie etwa 30 Jahre alt ist, hat sie sich unter iOS buchstäblich zu einem „Durchbruch“ entwickelt, da Sie mit multitouch beim Ziehen auf iOS frei mit dem Rest des Systems interagieren und Daten zum Zurücksetzen aus verschiedenen Anwendungen sammeln können.

iOS können mehrere Elemente gleichzeitig erfasst werden. Darüber hinaus müssen sie für die Auswahl nicht bequem zugänglich sein: Sie können das erste Objekt nehmen, dann zu einer anderen Anwendung gehen und etwas anderes greifen - alle Objekte werden auf einem „Stapel“ unter Ihrem Finger gesammelt. Rufen Sie dann das Universal Dock auf dem Bildschirm auf, öffnen Sie dort eine beliebige Anwendung und erfassen Sie das dritte Objekt. Wechseln Sie dann mit den ausgeführten Anwendungen zum Bildschirm und speichern Sie sie, ohne die Objekte freizugeben, in einem der geöffneten Programme. Eine solche Handlungsfreiheit ist auf dem iPad , auf dem iPhone ist die Drag & Drop Abdeckung in iOS auf das Framework einer Anwendung beschränkt.

Die beliebtesten Anwendungen ( Safary , Chrome , IbisPaint X , Mail , Photos , Files usw.) verfügen bereits über einen Drag & Drop Mechanismus. Darüber hinaus stellte Apple den Entwicklern eine sehr einfache und intuitive API zum Einbetten des Drag & Drop Mechanismus in Ihre Anwendung zur Verfügung. Der Drag & Drop Mechanismus funktioniert genau wie Gesten in UIView und verwendet das Konzept der Interaktionen , ähnlich wie Gesten. Sie können sich den Drag & Drop Mechanismus also einfach als eine wirklich leistungsstarke Geste vorstellen.

Es ist ebenso wie Gesten sehr einfach in Ihre Anwendung zu integrieren. Insbesondere, wenn Ihre Anwendung die UITableView- Tabelle oder die UICollectionView- Auflistung verwendet, da für sie API Drag & Drop verbessert und auf eine höhere Abstraktionsebene angehoben wird, da die Collection View Auflistung selbst Ihnen beim indexPath des Auflistungselements hilft, das Sie ziehen und ablegen möchten Drag . Sie weiß, wo sich Ihr Finger befindet, und interpretiert ihn als den Indexpfad des Sammlungselements, das Sie gerade ziehen. Ziehen Sie ihn als den Indexpfad des Sammlungselements, in dem Sie etwas ablegen . Die Collection View Auflistung liefert Ihnen also indexPath , ansonsten ist es absolut dieselbe API Drag & Drop wie für eine normale UIView .

Der Drag & Drop Prozess unter iOS besteht aus 4 verschiedenen Phasen:

Heben Sie an


Heben (Heben) - Dies ist der Fall, wenn der Benutzer eine lange Druckgeste ausführt, die das Element angibt, das "Drag & Drop" sein soll. In diesem Moment wird eine sehr leichte sogenannte " lift preview " des angezeigten Elements gebildet, und dann beginnt der Benutzer, seine Finger zu ziehen.



Ziehen (Drag & Drop)


Ziehen (Drag & Drop) - Dies ist, wenn der Benutzer das Objekt auf der Oberfläche des Bildschirms verschiebt. Während dieser Phase kann die " lift preview " für dieses Objekt geändert werden (ein grünes "+" Pluszeichen oder ein anderes Zeichen wird angezeigt) ...



... eine gewisse Interaktion mit dem System ist ebenfalls zulässig: Sie können auf ein anderes Objekt klicken und es der aktuellen Drag & Drop-Sitzung hinzufügen:



Drop


Ein Tropfen tritt auf, wenn der Benutzer einen Finger hebt. In diesem Moment können zwei Dinge passieren: Entweder wird das Drag Objekt zerstört oder das Drop Objekt wird am Ziel „fallen gelassen“.



Datenübertragung


Wenn der Drag-and-Drop-Vorgang nicht abgebrochen wurde und das Zurücksetzen des Drop- Vorgangs stattgefunden hat, erfolgt die Datenübertragung (Datenübertragung), bei der der Drop-Point Daten von der Quelle anfordert, und die asynchrone Datenübertragung erfolgt.

In diesem Tutorial zeigen wir Ihnen, wie Sie den Drag & Drop Mechanismus mithilfe der Demo-Anwendung "Bildergalerie" aus Stanfords Hausaufgabenkurs CS193P einfach in Ihre iOS App integrieren können .
Wir werden der Collection View Möglichkeit geben, uns AUSSERHALB mit Bildern zu füllen und INSIDE-Elemente mithilfe des Drag & Drop Mechanismus neu zu organisieren. Darüber hinaus wird dieser Mechanismus verwendet, um unnötige Elemente der Collection View in einen "Papierkorb" zu werfen , der eine normale UIView darstellt und durch eine Schaltfläche im Navigationsbereich dargestellt wird. Wir können in unserer Galerie gesammelte Bilder auch mithilfe des Drag & Drop Mechanismus für andere Anwendungen Drag & Drop , z. B. Notes oder Notes oder Mail oder eine Fotobibliothek ( Photo ).

Bevor ich mich jedoch auf die Implementierung des Drag & Drop Mechanismus in der Demo-Anwendung „Image Gallery“ konzentriere, werde ich kurz auf die Hauptkomponenten eingehen.

Funktionen der Demo-Anwendung "Image Gallery"


Die Benutzeroberfläche der Image Gallery-Anwendung ist sehr einfach. Dies ist ein „Bildschirmausschnitt“ des in den Navigation Controller eingefügten Image Gallery Collection View Controller für die Image Gallery Collection View Controller :



Der zentrale Teil der Anwendung ist sicherlich der Image Gallery Collection View Controller , der von der ImageGalleryCollectionViewController- Klasse mit dem Image Gallery-Modell als Variable var imageGallery = ImageGallery () unterstützt wird :



Das Modell wird durch eine struct ImageGallery- Struktur dargestellt, die ein Array von Bildbildern enthält, wobei jedes Bild durch eine struct ImageModel- Struktur beschrieben wird, die die URL Bildspeicherort- URL (wir werden das Bild selbst nicht speichern) und ihr Seitenverhältnis enthält:



Unser ImageGalleryCollectionViewController implementiert das DataSource- Protokoll:



Die benutzerdefinierte Zelle in der Zellsammlung enthält ein imageView- Bild : UIImageView! und Spinner- Aktivitätsindikator : UIActivityIndicatorView! und wird von der benutzerdefinierten subclass ImageCollectionViewCell der UICollectionViewCell- Klasse unterstützt:



Public API der ImageCollectionViewCell- Klasse ist die imageURL- Bild- URL . Sobald wir es installieren, wird unsere UI aktualisiert, dh die Daten für das Image werden in dieser imageURL asynchron ausgewählt und in der Zelle angezeigt. Während Daten aus dem Netzwerk abgerufen werden, funktioniert die Spinner- Aktivitätsanzeige und zeigt an, dass wir gerade Daten abrufen.

Ich verwende die globale Warteschlange (qos: .userInitiated) mit dem Argument qos Quality of Service, um Daten unter einer bestimmten URL abzurufen , die auf .userInitiated festgelegt ist, da ich die Daten auf Anfrage des Benutzers auswähle:



Jedes Mal, wenn Sie Ihre eigenen Variablen innerhalb eines Abschlusses verwenden, in unserem Fall imageView und imageURL , zwingt Sie der Compiler, sich selbst vor sie zu stellen . so dass Sie sich fragen: "Gibt es eine" Speicher-zyklische Verbindung "?" Wir haben hier keinen expliziten „ memory cycle “, weil das Selbst selbst keinen Zeiger auf diesen Abschluss hat.

Beim Multithreading sollten Sie jedoch berücksichtigen, dass die Zellen in der Collection View dank der Methode dequeueReusableCell wiederverwendbar sind. Jedes Mal, wenn eine Zelle (neu oder wiederverwendet) auf dem Bildschirm angezeigt wird, wird das Bild asynchron aus dem Netzwerk heruntergeladen (zu diesem Zeitpunkt dreht sich der „ Spinner “ der Spinner- Aktivitätsanzeige).

Sobald der Download abgeschlossen ist und das Bild empfangen wurde, wird die UI dieser Sammelzelle aktualisiert. Wir warten jedoch nicht auf das Laden des Bildes, sondern scrollen weiter durch die Sammlung, und die von uns markierte Sammlungszelle verlässt den Bildschirm, ohne unsere UI zu aktualisieren. Unten sollte jedoch ein neues Bild angezeigt werden, und dieselbe Zelle, die den Bildschirm verlassen hat, wird wiederverwendet, jedoch für ein anderes Bild, das die UI schnell laden und aktualisieren kann. Zu diesem Zeitpunkt kehrt das zuvor in dieser Zelle gestartete Laden des Bildes zurück und der Bildschirm wird aktualisiert, was zu einem falschen Ergebnis führt. Dies liegt daran, dass wir verschiedene Dinge ausführen, die mit dem Netzwerk in verschiedenen Threads funktionieren. Sie kommen zu unterschiedlichen Zeiten zurück.

Wie können wir die Situation beheben?
Im Rahmen des von uns verwendeten GCD- Mechanismus können wir den Download des Bildes der Zelle, die den Bildschirm verlassen hat, nicht abbrechen. Wenn unsere imageData- Daten aus dem Netzwerk eingehen , können wir jedoch die URL URL überprüfen, die das Laden dieser Daten verursacht hat, und sie mit der URL vergleichen, die der Benutzer haben möchte diese Zelle im Moment, d. h . imageURL . Wenn sie nicht übereinstimmen, aktualisieren wir die UI Zelle nicht und warten auf die benötigten Bilddaten:



Diese scheinbar absurde Codezeile url == self.imageURL sorgt dafür, dass in einer Umgebung mit mehreren Threads, die eine nicht standardmäßige Vorstellungskraft erfordert, alles korrekt funktioniert. Tatsache ist, dass einige Dinge in der Multithread-Programmierung in einer anderen Reihenfolge ablaufen, als der Code geschrieben wurde.

Wenn die Bilddaten nicht ausgewählt werden konnten, wird ein Bild mit einer Fehlermeldung in Form der Zeichenfolge "Fehler" und einem Emoji mit "Stirnrunzeln" generiert. Nur der leere Bereich in unserer Collection View kann den Benutzer ein wenig verwirren:



Wir möchten nicht, dass das Bild mit der Fehlermeldung das Seitenverhältnis dieses Fehlerbilds wiederholt, da in diesem Fall der Text zusammen mit dem Emoji gestreckt oder komprimiert wird. Wir möchten, dass es neutral ist - quadratisch, das heißt, es hat ein Seitenverhältnis nahe 1,0.



Wir müssen unseren Controller über diesen Wunsch informieren, damit er das Seitenverhältnis von Seitenverhältnis für den entsprechenden Indexpfad in seinem imageGallery- Modell korrigiert . Dies ist eine interessante Aufgabe, es gibt viele Möglichkeiten, sie zu lösen, und wir werden die einfachste auswählen - mit optionalem Verschluss var closeAspectRatio: (() -> Void)? . Es kann gleich Null sein und muss nicht installiert werden, wenn dies nicht erforderlich ist:



Beim Aufruf der Closure changeAspectRatio? () Bei fehlerhaftem Datenabruf verwende ich die optionale Kette. Jetzt kann jeder, der an einer Art von Einstellungen interessiert ist, wenn er ein fehlerhaftes Bild empfängt, diesen Abschluss auf etwas Bestimmtes setzen. Und genau das tun wir in unserem Controller in der cellForItemAt- Methode:



Details finden Sie hier .

Um Bilder mit dem richtigen Aspektverhältnis anzuzeigen , wird die sizeForItemAt- Methode des UICollectionViewDelegateFlowLayout- Delegaten verwendet :



Zusätzlich zur Collection View haben wir auf unserer UI im Navigationsbereich eine Leistenschaltfläche mit einem benutzerdefinierten GarbageView- Bild platziert, das einen "Papierkorb" als Unteransicht enthält :



In dieser Abbildung werden die Hintergrundfarben für GarbageView selbst und die UIButton- Schaltfläche mit dem Bild der "Mülltonne" (tatsächlich gibt es einen transparenten Hintergrund) speziell geändert, sodass Sie sehen, dass der Benutzer, der die Bilder der Galerie in die "Mülltonne" "entleert". viel mehr Handlungsspielraum beim „Ablegen“ von Drop als nur das Mülleimersymbol.
Die GarbageView- Klasse verfügt über zwei Initialisierer und beide verwenden die setup () -Methode:



In der setup () -Methode füge ich außerdem myButton als Unteransicht mit dem Bild der "Mülltonne" hinzu, das aus dem Standard- Bar Button :



Ich habe einen transparenten Hintergrund für GarbageView festgelegt :



Die Größe der Mülltonne und ihre Position werden in der layoutSubviews () -Methode der UIView- Klasse abhängig von den Grenzen der angegebenen UIView bestimmt :



Dies ist die erste Version der Demo-Anwendung "Image Gallery". Sie befindet sich auf Github im Ordner " ImageGallery_beginning ". Wenn Sie diese Version der Anwendung "Bildergalerie" ausführen, sehen Sie das Ergebnis der Anwendung, die an Testdaten arbeitet, die wir anschließend löschen und die "Bildergalerie" ausschließlich AUSSEN ausfüllen:



Der Plan zur Implementierung des Drag & Drop Mechanismus in unserer Anwendung lautet wie folgt:

  1. Zunächst werden wir unserer Collection View Möglichkeit geben, Drag- from-UIImage- Bilder sowohl extern als auch lokal zu ziehen.
  2. Dann werden wir unserer Sammlung von Collection View beibringen, "gezogenes" Drag extern oder lokal von UIImage zu akzeptieren.
  3. Wir werden auch unsere GarbageView mit der Mülltonne-Schaltfläche lehren, UIImage- Bilder zu akzeptieren, die aus der lokalen Collection View und sie aus der Collection View entfernen


Wenn Sie am Ende dieses Tutorials alle erforderlichen Codeänderungen vornehmen, erhalten Sie die endgültige Version der Demoanwendung „Image Gallery“, in der der Drag & Drop Mechanismus implementiert wurde. Es befindet sich auf Github im Ordner ImageGallery_finished .

Die Leistung des Drag & Drop Mechanismus in Ihrer Collection View von zwei neuen Delegierten bereitgestellt.
Die Methoden des ersten Delegaten, dragDelegate , sind so konfiguriert, dass die Drag & Drop- Drags initialisiert und angepasst werden.
Die Methoden des zweiten Delegaten, dropDelegate , vervollständigen das Ziehen und Ablegen von Drags und bieten im Grunde genommen Datenübertragungs- und benutzerdefinierte Animationseinstellungen, wenn Drop zurückgesetzt wird, sowie andere ähnliche Dinge.

Es ist wichtig zu beachten, dass beide Protokolle völlig unabhängig sind. Sie können das eine oder andere Protokoll verwenden, wenn Sie nur Drag oder nur Drop Drop müssen. Sie können jedoch beide Protokolle gleichzeitig verwenden und Drag & Drop gleichzeitig Drag & Drop , wodurch zusätzliche Funktionen eröffnet werden Drag & Drop Mechanismus zum Neuordnen von Elementen in Ihrer Collection View .

Drag & Drop Drag Elemente aus der Collection View


Das Implementieren des Drag Protokolls ist sehr einfach. Das erste, was Sie immer tun sollten, ist, sich selbst als DragDelegate- Delegat festzulegen :



Und ganz oben in der ImageGalleryCollectionViewController- Klasse sollten Sie natürlich "Ja" sagen. Wir implementieren das UICollectionViewDragDelegate- Protokoll:



Sobald wir dies tun, beginnt der Compiler sich zu beschweren, wir klicken auf den roten Kreis und werden gefragt: "Möchten Sie die erforderlichen Methoden des UICollectionViewDragDelegate- Protokolls hinzufügen ?"
Ich antworte: "Natürlich will ich!" und klicken Sie auf die Schaltfläche Fix :



Die einzige erforderliche Methode des UICollectionViewDragDelegate- Protokolls ist die itemsForBeginning- Methode, die dem Drag System mitteilt , dass wir sie ziehen und ablegen . Die Methode itemsForBeginning wird aufgerufen, wenn der Benutzer beginnt, eine Zelle in der Auflistungszelle zu "ziehen" (zu ziehen).

Beachten Sie, dass bei dieser Methode die Collection View Auflistung indexPath hinzugefügt hat . Dies sagt uns, welches Element der Sammlung, welchen Indexpfad wir ziehen und ablegen werden. Dies ist für uns sehr praktisch, da die Anwendung für die Verwendung der Argumente session und indexPath verantwortlich ist , um herauszufinden , wie mit diesem Drag & Drop von Drag umgegangen werden soll .

Wenn das Array [UIDragItems] der " ziehbaren " Elemente zurückgegeben wird, wird das "Ziehen" des Ziehens initialisiert. Wenn das leere Array [] zurückgegeben wird, wird das "Ziehen" des Drag ignoriert.

Ich werde eine kleine private DragItems-Funktion (at: indexPath) mit dem Argument indexPath erstellen . Es gibt das Array [UIDragItem] zurück, das wir benötigen .



Wie sieht ein Drag & Drop- UIDragItem aus ?
Er hat nur eine sehr wichtige Sache namens itemProvider . itemProvider ist nur etwas, das Daten bereitstellen kann, die gezogen werden.

Und Sie haben das Recht zu fragen: "Was ist mit dem" Ziehen und Ablegen "eines UIDragItem- Elements, das einfach keine Daten enthält?" Das Element, das Sie ziehen möchten, enthält möglicherweise keine Daten, da das Erstellen dieser Daten ein kostspieliger Vorgang ist. Dies kann ein Bild sein oder etwas, das das Herunterladen von Daten aus dem Internet erfordert. Das Tolle ist, dass der Drag & Drop Vorgang vollständig asynchron ist. Wenn Sie mit dem Ziehen und Ablegen beginnen, ist Drag wirklich ein sehr leichtes Objekt ( lift preview ). Sie ziehen es überall hin und während dieses "Ziehens" passiert nichts. Sobald Sie Ihr Objekt jedoch „ ablegen“ , muss es als itemProvider Ihr "gezogenes" und "geworfenes" Objekt wirklich mit realen Daten versorgen, selbst wenn dies eine gewisse Zeit in Anspruch nimmt.

Glücklicherweise gibt es viele integrierte itemProvider . Hierbei handelt es sich um Klassen, die bereits in iOS und itemPoviders sind , z. B. NSString , mit dem Sie Text ohne Schriftarten ziehen und NSString können. Dies ist natürlich ein UIImage- Bild. Sie können UIImages- Bilder überall auswählen und ziehen und ablegen . Die NSURL- Klasse, die absolut wunderbar ist. Sie können zur Web gehen, die URL auswählen und sie an einer beliebigen Stelle ablegen. Dies kann ein Link zu einem Artikel oder eine URL für ein Bild sein, wie es in unserer Demo sein wird. Dies sind die Farbklassen von UIColor , MKMapItem- Kartenelement, CNContact- Kontakt aus dem Adressbuch. Sie können viele Dinge auswählen und ziehen. Alle von ihnen sind itemProvider .

Wir werden das UIImage- Bild per Drag & Drop verschieben . Es befindet sich in der Zellenzelle der Collection View mit indexPath , wodurch ich die Zellenzelle auswählen, die Outlet Bildansicht daraus abrufen und das Bildbild abrufen kann .

Lassen Sie uns diese Idee mit ein paar Codezeilen ausdrücken.
Zuerst fordere ich meine Collection View zu einer Zelle für das Element an, das diesem Indexpfad entspricht .



Die cellForItem (at: IndexPath) -Methode für die Collection View funktioniert nur für sichtbare Zellen, aber in unserem Fall funktioniert sie natürlich, da ich das Drag Sammlungselement auf den Bildschirm ziehe und ablege und es sichtbar ist.

Also habe ich eine "ziehbare" Zellzelle.
Als nächstes benutze ich den als Operator ? zu dieser Zelle, so dass es einen TYP meiner benutzerdefinierten subclass . Und wenn dies funktioniert, erhalte ich eine Outlet ImageView , aus der ich das Image- Image entnehme. Ich habe gerade das Bild für diesen indexPath "aufgenommen".

Nachdem ich nun ein Bildbild habe, muss ich nur noch eines dieser UIDragItems erstellen, indem ich das resultierende Bild als itemProvider verwende , dh das, was uns die Daten liefert.
Ich kann dragItem mit dem UIDragItem- Konstruktor erstellen, der itemProvider als Argument verwendet:



Anschließend erstellen wir einen itemProvider für das Bild , auch mit dem NSItemProvider- Konstruktor. Es gibt mehrere Konstruktoren für NSItemProvider , aber unter ihnen gibt es einen wirklich wunderbaren - NSItemProvider (Objekt: NSItemProviderWriting) :



Sie geben das Objektobjekt einfach an diesen NSItemProvider- Konstruktor weiter, und er weiß, wie itemProvider daraus erstellt wird. Als solches Objekt gebe ich dem Bild das Bild , das ich von der Zellenzelle erhalten habe, und erhalte itemProvider für UIImage .
Und das ist alles. Wir haben dragItem erstellt und sollten es als Array mit einem Element zurückgeben.

Aber bevor ich wieder dragItem , ich werde noch eine Sache tun, nämlich die Variable gesetzt localObject für dragItem gleich das resultierende Bild Bild .



Was bedeutet das?
Wenn Sie Draglokal "Drag & Drop" ausführen , dh innerhalb Ihrer Anwendung, müssen Sie nicht den gesamten mit itemProvider verknüpften Code durch asynchrones Abrufen von Daten durchgehen. Sie müssen dies nicht tun, Sie müssen nur localObject nehmen und es verwenden. Dies ist eine Art „Kurzschluss“ mit lokalem „Drag & Drop“ Drag.

Der Code, den wir geschrieben haben, funktioniert beim „Ziehen“ Dragaußerhalb unserer Sammlung Collection Viewin andere Anwendungen. Wenn wir jedoch Draglokal „ziehen“ , können wir localObject verwenden . Als nächstes gebe ich ein Array von einem dragItem- Element zurück .

Übrigens, wenn ich aus irgendeinem Grund nicht bekommen konnte Bild für diese Zelle Zelle , kehre ich leeres Array [] , das bedeutet , dass die „drag“ Dragabgebrochen.



Neben lokalen Objekt localObject , können Sie den lokalen Kontext erinnern localContext für unsere DragSitzung die Sitzung . In unserem Fall wird es eine Sammlung von seiner Kollektion und es ist nützlich , um uns danach:



Mit „Drag and Drop“ Dragkönnen Sie weitere Elemente hinzufügen , Elemente diesen „drag and drop“, nur die Geste tun tippt auf sie. Als Ergebnis können Sie ziehenDragviele Gegenstände gleichzeitig. Und dies ist einfach mit einer anderen Delegatmethode zu implementieren , UICollectionViewDragDelegate , die der Methode itemsForeginning , einer Methode mit dem Namen itemsForAddingTo , sehr ähnlich ist . Die itemsForAddingTo- Methode sieht genauso aus wie die itemsForeginning- Methode und gibt genau das Gleiche zurück, da sie uns auch einen Indexpfad darüber gibt, was der Benutzer während des Drag Drag& Drop-Vorgangs „ aufgezeichnet “ hat , und ich muss nur das Bild aus der Zelle abrufen die der Benutzer "abgeklebt" und zurückgegeben. Gibt



ein leeres Array [] von der itemsForAddingTo- Methode zurückbewirkt, dass die Tippgeste auf die übliche Weise interpretiert wird, dh als Auswahl dieser Zelle .
Und das ist alles, was wir zum Ziehen und Ablegen benötigen Drag.
Wir starten die Anwendung.
Ich wähle das Bild "Venedig" aus, halte es eine Weile und beginne



mich zu bewegen ... ... und wir können dieses Bild wirklich in die Anwendung ziehen Photos, da Sie das grüne Pluszeichen "+" in der oberen linken Ecke des "ziehbaren" Bildes sehen. Ich kann eine Tap- Geste auf ein anderes Artika-Bild aus der Sammlung ausführen Collection View...



... und jetzt können wir zwei Bilder in die Anwendung einfügen Photos:



Da der PhotosMechanismus bereits in die Anwendung integriert istDrag & Dropdann funktioniert alles gut und es ist cool.
Das "Ziehen" Dragund "Ablegen" des DropGaleriebilds in andere Anwendungen funktioniert für mich . Ich musste in meiner Anwendung nicht viel tun, außer das Bild als Array [UIDragItem] zu liefern . Dies ist eines der vielen großartigen Merkmale des Mechanismus Drag & Drop- es ist sehr einfach, ihn in beide Richtungen arbeiten zu lassen.

DropBilder auf Sammlung zurücksetzenCollection View


Jetzt müssen wir einen DropTeil für meine Sammlung erstellen Collection View, damit wir Dropalle "gezogenen" Bilder IN dieser Sammlung "ausgeben" können. Ein "ziehbares" Bild kann von AUSSEN oder direkt IN dieser Sammlung "kommen".
Um dies zu tun, werden wir das gleiche tun getan zu delegieren dragDelegate , also machen wir selbst, die selbst delegieren dropDelegate in dem Verfahren von dem viewDidLoad :



Auch hier müssen wir an die Spitze unserer Klasse klettern ImageGalleryCollectionViewController und Protokoll - Implementierung überprüfen UICollectionViewDropDelegate :



Sobald wir unser neues Protokoll hinzugefügt hatten, begann der Compiler erneut zu "beschweren", dass wir dieses Protokoll nicht implementiert haben. Wir klicken auf die Schaltfläche Fixund die erforderlichen Methoden dieses Protokolls erscheinen vor uns. In diesem Fall werden wir darüber informiert, dass wir die performDrop- Methode implementieren müssen :



Wir müssen dies tun, sonst wird kein „Reset“ durchgeführt Drop. Tatsächlich werde ich die performDrop- Methode zuletzt implementieren , da es einige andere sehr empfohlene AppleMethoden gibt, die Sie für das DropTeil implementieren müssen . Dies ist canHandle und dropSessionDidUpdate :



Wenn wir diese beiden Methoden implementieren, können wir ein wenig Greenbacks erhalten Plus - Zeichen „+“ , wenn wir Sie Bilder von außen auf unserer Sammlung ziehen ollection View, und darüber hinaus werden wir nicht versuchen , zu entleeren , was wir nicht verstehen.

Lassen Sie uns implementieren canHandle . Wir müssen Ihre Version des Verfahrens canHandle , die für die Sammlung bestimmt ist ollection View., aber diese Methode ollection Viewsieht genau die gleiche Art und Weise wie ein ähnliches Verfahren zu dem herkömmlichen einem UIView , gibt es keine indexPath wir Rückkehr gerade brauchen. session.canLoadObjects (ofClass: UIImage.self) , und dass die Mittel dass ich das "Zurücksetzen" von Objekten dieses Cl akzeptiere PAS in meiner Sammlung ollection View:



Dies reicht jedoch nicht aus, um das DropBild Collection ViewAUSSERHALB in meine Sammlung zu "kopieren" .
Wenn das „Reset“ Dropdas Bild innerhalb der Sammlung nimmt Collection View, wird der Benutzer seine eigenen Elemente reorganisieren Gegenstände durch den Mechanismus Drag & Drop, dann nur ein Bild einer UIImage und Implementierung des Verfahrens canHandle wie in der oben beschriebenen Weise aussehen wird.

Wenn das "Dumping" des DropBildes jedoch AUSSEN erfolgt, sollten wir nur das "Ziehen und Ablegen" behandeln Drag, das das UIImage- Bild zusammen mit URLdiesem Bild ist, da wir die UIImage- Bilder nicht direkt speichern werdenim Modell. In diesem Fall werde ich zurückkehren wahr in dem Verfahren canHandle nur dann , wenn beide Bedingungen ein paar durchgeführt session.canLoadObjects (ofClass: NSURL.self) && session.canLoadObjects (ofClass: UIImage.self) :



ich noch habe , um festzustellen , ob ich mit zu tun habe „Reset“ AUSSEN ODER INNEN. Ich werde es tun , mit Hilfe von berechneten Konstanten isself , für die die Berechnung kann ich diese Art der Sache in Verwendung Dropder Sitzung die Sitzung sowohl die lokale DragSitzung localDragSession . Diese lokale DragSitzung hat wiederum einen lokalen Kontext localContext .
Wenn Sie sich erinnern, setzen wir diesen lokalen Kontext in der MethodeitemsForVeginning Drag Delegierten UICollectionViewDragDelegate :



Ich werde die lokalen Kontext erforschen localContext auf Gleichheit in meiner Sammlung Collection . Richtig , TYPE von localContext ist Any , und ich muss ein Casting von TYPE Any mit dem Operator as durchführen. UICollectionView :



Wenn der lokale Kontext (session.localDragSession .localContext als UICollectionView ??) Ist meine Sammlung Collection , die erweiterte Variable isSelf ist wahrund es gibt ein lokales "Zurücksetzen" IN meiner Sammlung. Wenn diese Gleichheit verletzt wird, handelt es sich um ein "Zurücksetzen" DropAUSSEN.

Die canHandle- Methode gibt an , dass wir diese Art von Drag & Drop nur für Dragunsere Sammlung verarbeiten können Collection View. Ansonsten macht es keinen Sinn, von "Dumping" zu sprechen Drop.

Wenn wir auf „Reset“ fortsetzen Drop, ist es noch bis zu dem Moment , dass der Benutzer die Finger vom Bildschirm heben wird , und es wird ein echter „Reset“ sein Drop, müssen wir berichten , iOSunter Verwendung des Verfahrens dropSessionDidUpdate Delegierten UICollectionViewDropDelegate über unser Angebot UIDropProposal zurückgesetzt zu implementieren Drop.

Bei dieser Methode müssen wir einen DropSatz zurückgeben, der für das Operationsargument die Werte .copy oder .move oder .cancel oder .forbidden enthalten kann . Und dies sind alle Möglichkeiten, die wir im Normalfall haben, wenn wir uns mit einem regulären UIView befassen . Die Sammlung geht jedoch noch weiter und bietet an, das spezielle Angebot UICollectionViewDropProposal zurückzugeben , eine Klasse von UIDropProposal, mit der Sie zusätzlich zur Operationsoperation einen zusätzlichen Intent- Parameter für die Sammlung angeben können . Parameter

Collection ViewsubclassCollection View

Mit Absicht wird der Sammlung mitgeteilt ,Collection Viewob das "verworfene" Element in einer vorhandenen Zelle platziert werden soll oder ob eine neue Zelle hinzugefügt werden soll . Sehen Sie den Unterschied? Im Fall der SammlungCollection Viewverkünden wir unsere Absicht, eine Absicht .

In unserem Fall möchten wir immer eine neue Zelle hinzufügen, damit Sie sehen, wie hoch unser Intent- Parameter ist.
Wir wählen den zweiten Konstruktor für UICollectionViewDropProposal aus :



In unserem Fall möchten wir immer eine neue Zelle hinzufügen, und der Intent- Parameternimmtim Gegensatz dazuden Wert .insertAtDestinationIndexPath an.insertIntoDestinationIndexPath .



Ich habe wieder die berechnete Konstante isSelf verwendet , und wenn es sich um eine Selbstreorganisation handelt , bewege ich .move , andernfalls kopiere ich .copy . In beiden Fällen verwenden wir .insertAtDestinationIndexPath , d.H. Das Einfügen neuer Zellen .

Bisher habe ich die performDrop- Methode noch nicht implementiert, aber schauen wir uns an, was eine Sammlung bereitsCollection Viewmit dieser kleinen Informationtun kann, die wir ihr zur Verfügung gestellt haben.

Ich ziehe das Bild ausSafarider SuchmaschineGoogleÜber diesem Bild wird ein grünes „+“ angezeigt, das darauf hinweist, dass unsere Bildergalerie nicht nur bereit ist, dieses Bild zu akzeptieren und zu kopieren URL, sondern auch einen Platz in der Sammlung bietet Collection View:



Ich kann auf ein weiteres Bildpaar in Safariund klicken Es werden bereits 3 "gezogene" Bilder angezeigt:



Wenn ich jedoch meinen Finger hebe und Dropdiese Bilder "fallen lasse " , werden sie nicht in unserer Galerie platziert, sondern kehren einfach zu ihren vorherigen Positionen zurück , da wir die performDrop- Methode noch nicht implementiert haben .



Sie konnten sehen, dass die Sammlung Collection Viewbereits weiß, was ich tun möchte.
Die Sammlung Collection Viewist eine absolut wunderbare Sache für den Mechanismus.Drag & DropSie hat dafür sehr mächtige Funktionen. Wir haben sie kaum berührt, indem wir 4 Codezeilen geschrieben haben, und sie ist in der Wahrnehmung von "Zurücksetzen" bereits ziemlich weit gegangen Drop. Kehren
wir zum Code zurück und implementieren die performDrop- Methode .



Bei dieser Methode kommen wir mit 4 Codezeilen nicht aus, da die performDrop- Methode etwas komplizierter, aber nicht zu viel ist.
Wenn ein „Reset“ Drop, dann ist die Methode performDrop müssen wir unser Modell aktualisieren, die eine Galerie von Bildern Bildergalerie Combo Bild Bildern , und wir müssen unsere visuelle Sammlung aktualisieren Collection .

Wir haben zwei verschiedene "Reset" -Szenarien Drop.

Wenn Dropaus meiner collectionView- Sammlung ein "Zurücksetzen" erfolgt , muss ich das DropSammlungselement an einer neuen Stelle "zurücksetzen" und von der alten Stelle entfernen, da ich in diesem Fall dieses Sammlungselement verschiebe ( .move ). Dies ist eine triviale Aufgabe.

Es wird ein „Reset“ Dropdurchgeführt wird aus einer anderen Anwendung heraus, dann müssen wir die Eigenschaft verwenden ItemProvider «gezogen» Element Element , um Daten abzurufen.

Wenn wir Dropin der collectionView- Sammlung einen „Reset“ durchführen , stellt uns die Sammlung einen Koordinator- Koordinator zur Verfügung. In erster Linie haben wir berichtet , den Koordinator Koordinator , es destinationIndexPath , dh indexPath „-destination“, „Reset“ Drop, dass dort , wo wir werden ein „Reset“ .



Aber destinationIndexPath kann gleich Null , so dass Sie die „rücksetzbar“ Bild in diesem Teil der Sammlung kann ziehen Collection View, die zwischen einigen bestehenden Zellen kein Fleck Zellen , so dass es gut sein kann null . In diesem Fall erstelle ich einen Indexpfad mit dem Element 0th item im Abschnitt 0th section .



Ich könnte jeden anderen Indexpfad auswählen , aber ich werde diesen Indexpfad standardmäßig verwenden.

Jetzt wissen wir, wo wir den „Reset“ durchführen werden Drop. Wir müssen gehen durch alle „blow-off“ Artikel coordinator.items , vom Koordinator zur Verfügung gestellt Koordinator . Jedes Element aus dieser Liste hat einen UICollectionViewDropItem- TYP und kann uns sehr interessante Informationen liefern.

Zum Beispiel, wenn ich bekommen kann sourceIndexPath von item.sourceIndexPath , werde ich genau wissen , was es ist „Drag and Drop“ Dragvon selbst erledigt ist, die Selbstund die Quelle des ZiehensDrag ist das Sammlungselement mit indexPath gleich sourceIndexPath : In diesem Fall muss



ich nicht einmal auf localContext schauen , um herauszufinden, dass dieses "Ziehen und Ablegen" IN der Sammlung collectionView durchgeführt wurde . Wow!

Jetzt kenne ich die Quelle sourceIndexPath und das Ziel " destinationIndexPath " Drag & Drop, und die Aufgabe wird trivial. Alles , was ich tun muß , ist das Modell zu aktualisieren , so dass die Quelle und der „Zielpunkt“ umgekehrt wird , und aktualisieren Sie dann die Sammlung Collection , das das Element aus der Sammlung müssen entfernen sourceIndexPath und fügen Sie ihn in der Sammlung destinationIndexPath .

Unser lokaler Fall ist der einfachste, da in diesem Fall der Mechanismus Drag & Dropnicht nur in derselben Anwendung, sondern in derselben collectionView- Sammlung funktioniert und ich alle erforderlichen Informationen über den Koordinatorkoordinator abrufen kann. Lassen Sie es uns in diesem einfachsten lokalen Fall implementieren:



In unserem Fall benötige ich nicht einmal localObject , das ich zuvor beim Erstellen von dragItem "versteckt" habe und das ich jetzt aus dem "gezogenen" Element in der Elementauflistung im Formular item.localObject ausleihen kann . Wir werden es brauchen, wenn wir DropBilder in den "Papierkorb" werfen, der sich in derselben Anwendung befindet, aber nicht dieselbe collectionView- Sammlung ist . Jetzt nur ich brauche zwei IndexPathes : Quelle sourceIndexPath und „Zielpunkt“ destinationIndexPath .

Ich bekomme zuerst InformationenimageInfo über das Bild an der alten Stelle aus dem Modell, entfernen Sie es von dort. Und fügenanschließend in ein Array von Bildern meiner Modelle Bildergalerie Informationen Imageinfo ein Bild mit einem neuen Index destinationIndexPath.item . So habe ich mein Modell aktualisiert:



Jetzt muss ich die collectionView- Sammlung selbst aktualisieren. Es ist sehr wichtig zu verstehen, dass ich nicht alle Daten in meiner collectionView- Sammlung mit reloadData () während des Drag & Drop-Prozessesüberladen möchteDrag, da dadurch die gesamte "Welt" unserer Bildergalerie neu installiert wird, was sehr schlecht ist. TUN SIE ES NICHT. Stattdessen werde ich aufräumen und Elemente einfügenElemente einzeln:



Ich habe das Sammlungselement collectionView mit sourceIndexPath gelöscht und ein neues Sammlungselement mit destinationIndexPath eingefügt.

Es sieht so aus, als ob dieser Code großartig funktioniert, aber in Wirklichkeit kann dieser Code Ihre Anwendung zum Absturz bringen. Der Grund dafür ist, dass Sie zahlreiche Änderungen an Ihrer collectionView- Sammlung vornehmen.In diesem Fall muss jeder Schritt zum Ändern der Sammlung normal mit dem Modell synchronisiert werden, was in unserem Fall nicht beobachtet wird, da beide Vorgänge gleichzeitig ausgeführt werden: Löschen und Einfügen. Daher die Sammlung collectionViewwird sich irgendwann in einem NICHT synchronisierten Zustand mit dem Modell befinden.

Aber es gibt eine wirklich coole Möglichkeit, dies zu umgehen : Die collectionView- Auflistung verfügt über eine Methode namens performBatchUpdates mit einem Abschluss ( closure). Innerhalb dieses Abschlusses kann ich eine beliebige Anzahl dieser deleteItems , insertItems , moveItems und alles, was ich möchte, platzieren:



Jetzt werden deleteItems und insertItems als eine einzige Operation ausgeführt, und es wird nie an einer Synchronisierung Ihres Modells mit der Collection collectionView mangeln .

Und schließlich müssen wir den Koordinator bitten, das „Zurücksetzen“ selbst zu implementieren und zu animieren Drop:



Sobald Sie Ihren Finger vom Bildschirm heben, bewegt sich das Bild, alles geschieht gleichzeitig: „Zurücksetzen“, das Bild verschwindet an einem Ort und Aussehen an einem anderen.
Versuchen wir, das Testbild „Venedig“ in unserer Bildergalerie an das Ende der ersten Zeile zu verschieben ...



... und es zurückzusetzen:



Wie wir wollten, wurde es am Ende der ersten Zeile platziert.
Hurra!Alles arbeitet!

Jetzt werden wir uns NICHT mit dem lokalen Fall befassen, dh wenn das "Zurücksetzen" -Element AUSSERHALB kommt, dh aus einer anderen Anwendung.
Um dies zu tun in dem Code schreiben wir sonst in Bezug auf die sourceIndexPath . Wenn wir nicht über sourceIndexPath verfügen , bedeutet dies, dass das "zurücksetzbare" Element von AUSSEN stammt und wir die Datenübertragung mit dem itemProver des zurücksetzbaren item.dragItem.itemProvider- Elements verwenden müssen :



Wenn Sie AUSSEN "ziehen Dragund ablegen " und " ablegen" ”DropWerden diese Informationen dann sofort verfügbar? Nein, Sie wählen ASYNCHRON Daten aus dem "gezogenen" Objekt aus. Aber was ist, wenn die Probe 10 Sekunden dauert? Was wird die Sammlung zu diesem Zeitpunkt tun ollection View? Darüber hinaus kommen die Daten möglicherweise nicht in der Reihenfolge an, in der wir sie angefordert haben. Dies zu handhaben ist nicht einfach und Appleschlug für ollection Viewdiesen Fall eine völlig neue Technologie für die Verwendung von Ersatzstoffen vor Placeholders.

Sie platzieren einen Collection ViewPlatzhalter in Ihrer Sammlung Placeholder, und die Sammlung Collection Viewverwaltet alles für Sie. Wenn die Daten endgültig ausgewählt sind, müssen Sie den Platzhalter lediglich auffordern, Placeholderseinen placeholderContext- Kontext aufzurufenund sagen Sie ihm, dass Sie die Informationen erhalten haben. Aktualisieren Sie dann Ihr Modell und Ihren Kontext placeholderContext tauscht die Zelle AUTOMATISCH mit dem Platzhalter Placeholdergegen eine Ihrer Zellen aus , was dem Datentyp entspricht, den Sie erhalten haben.

Alle diese Maßnahmen , die wir produzieren , durch einen Platzhalter Kontext zu schaffen placeholderContext , die einen Platzhalter steuert Placeholderund dass Sie von Koordinator erhalten Koordinator , „Reset“ zu fragen DropElement Element an einen Platzhalter Placeholder.

Ich werde den Initialisierer für den Platzhalterkontext placeholderContext verwendenDas "wirft" DragItem in einen UICollectionViewDropPlaceholder :



Das Objekt, das ich "werfen" werde, Dropist item.dragItem , wobei item ein for- Element einer Schleife ist, da wir Dropviele koordinator.items werfen können . Wir "werfen" sie eins nach dem anderen. Also, item.dragItem - das ist , was wir „ziehen“ sind Dragund „werfen“ Drop. Das nächste Argument für diese Funktion ist der Platzhalter, und ich werde ihn mit dem UICollectionViewDropPlaceholder- Initialisierer erstellen :



Dazu muss ich wissen, wo ich den Platzhalter einfügen werdePlaceholderd.h. insertionIndexPath sowie die Kennung der wiederverwendeten Zelle reuseIdentifier .
Das insertionIndexPath- Argument entspricht offensichtlich dem destinationIndexPath . Es ist der IndexPath zum Platzieren des "gezogenen" Objekts und wird ganz am Anfang der performDropWith- Methode berechnet .

Schauen wir uns nun die Zellen- ID von reuseIdentifier an . SIE müssen entscheiden, welcher Zelltyp Ihr Locator ist Placeholder. Im Brennpunkt Koordinator nicht „vorgerüstete“ Zelle - Zelle an einen PlatzhalterPlaceholder. Dass Sie eine Entscheidung über die Zelle nehmen müssen Zelle . Daher wird die Kennung der wiederverwendeten Zelle reuseIdentifiercell von Ihnen angefordert,storyboard damit sie als PROTOTYP verwendet werden kann.

Ich werde es "DropPlaceholderCell" nennen, aber im Grunde könnte ich es wie auch immer nennen.
Es ist nur ein String String , die ich auf meinem verwenden werde storyboarddiese Sache zu schaffen.
Gehen Sie zurück zu unserer storyboardund erstellen Sie eine Zellzelle für den Platzhalter Placeholder. Dazu müssen wir nur eine Sammlung auswählen Collection Viewund untersuchen. Im allerersten Feld Itemswechsle ich 1zu2. Dadurch entsteht für uns sofort eine zweite Zelle, die eine exakte Kopie der ersten ist.



Wir wählen unsere neue Zelle aus ImageCell, setzen den Bezeichner " DropPlaceholderCell", löschen alle UIElemente von dort , einschließlich Image View, da dieser PROTOTYP verwendet wird, wenn das Bild noch nicht angekommen ist. Wir fügen dort einen neuen Aktivitätsindikator aus der Objektpalette hinzu Activity Indicator, der sich dreht und die Benutzer darüber informiert, dass ich einige "Zurücksetzen" -Daten erwarte. Ändern Sie auch die Hintergrundfarbe Backgroundzu verstehen , dass , wenn „Reset“ von außen Bild funktioniert genau diese Zelle Zelle als Prototypen:



Neben der Art der neuen Zelle sollte nicht ImageCollectionVewCell, weil es keine Bilder darin geben wird. Ich werde diese Zelle zu einer normalen UIollectionCiewCell TYPE- Zelle machen , da wir keine Outletsfür die Steuerung benötigen :



Konfigurieren Sie den Aktivitätsindikator Activity Indicatorso, dass er von Anfang an animiert wird und ich nichts in den Code schreiben müsste, um ihn zu starten. Klicken Sie dazu auf die Option Animating:



Und das ist alles. Also haben wir alle Einstellungen für diese Zelle vorgenommen DropPlaceholderCellund kehren zu unserem Code zurück. Jetzt haben wir einen großartigen Locator Placeholderbereit.

Alles, was wir tun müssen, ist, die Daten abzurufen. Wenn die Daten empfangen werden, teilen wir dem placeholderContext einfach diesen Kontext mit und er tauscht den Platzhalter ausPlaceholderund unsere "native" Zelle mit Daten, und wir werden Änderungen am Modell vornehmen.

Ich werde EIN Objekt "laden", das mein Element sein wird, indem ich die Methode loadObject (ofClass: UIImage.self) (Singular) verwende. Ich verwende den Code item.dragItem.itemProvider Anbieter ItemProvider , welche Datenelemente liefert Artikel asynchron. Es ist klar, dass wenn iitemProvider verbunden ist , wir das iitem-Objekt "reset" außerhalb dieser Anwendung erhalten. Das Folgende ist die Methode loadObject (ofClass: UIImage.self) (Singular):



Dieser bestimmte Abschluss wird NICHT ausgeführtmain queue. Und leider mussten wir Schalter main queuemit DispatchQueue.main.async {} , um „fangen“ das Seitenverhältnis des Bildes auf eine lokale Variable aspectratio .

Wir haben wirklich zwei lokale Variablen imageURL und AspectRatio eingeführt ...



... und wir werden sie beim Laden des Image- Images und der URL- URL "abfangen" :



Wenn beide lokalen Variablen imageURL und AspectRatio nicht Null sind , werden wir den Platzhalter- Kontext- Platzhalter- Kontext mit der Methode commitInsertion fragenGeben Sie uns die Möglichkeit, unser imageGallery- Modell zu ändern :



In diesem Ausdruck haben wir insertionIndexPath - dies ist der einzufügende indexPath , und wir ändern unser imageGallery- Modell . Das ist alles, was wir tun müssen, und diese Methode ersetzt AUTOMATISCH einen Platzhalter Placeholderdurch eine Zelle, indem sie die normale cellForItemAt- Methode aufruft .

Beachten Sie, dass insertionIndexPath sich stark von destinationIndexPath unterscheiden kann . Warum?Da die Datenerfassung natürlich 10 Sekunden dauern kann, ist dies unwahrscheinlich, kann jedoch 10 Sekunden dauern. In dieser Zeit Collection Viewkann in der Sammlung viel passieren. Können neue Zellen hinzufügen Zellen geschieht alles schnell genug.

Verwenden Sie IMMER insertionIndexPath und NUR insertionIndexPath , um Ihr Modell zu aktualisieren.

Wie aktualisieren wir unser Modell?

Wir fügen in das Array imageGallery.images Struktur imagemodel , bestehend aus dem Seitenverhältnis aspectratio und Bild - URL imageURL , der uns die entsprechenden zurück nach Anbietern .

Dadurch wird unser imageGallery- Modell aktualisiert , und die Methode commitInsertion erledigt den Rest für uns. Sie müssen nichts mehr tun, keine Einfügungen, keine Löschungen, nichts davon. Und da wir uns in einer Schließung befinden, müssen wir uns natürlich selbst hinzufügen . .



Wenn wir aus irgendeinem Grunde nicht in der Lage sind das Seitenverhältnis zu erhalten aspectratio und URLBild imageURL aus den entsprechenden durch Anbieter , könnte ein Fehler empfangen wurde Fehler anstelle von Anbietern , müssen wir wissen lassen, sie aus dem Zusammenhang placeholderContext , müssen Sie diese Platzhalter zerstören Placeholder, weil wir alle gleich sind, können wir nicht Andere Daten abrufen:



Eine Sache, die Sie beachten sollten, ist, URLsdass sie von Orten wie diesem stammen Google. In Wirklichkeit benötigen sie kleinere Transformationen, um „sauber“ zu werden.URLfür das Bild. Wie dieses Problem gelöst wird, können Sie dieser Demoanwendung in einer Datei Utilities.swiftauf Github entnehmen .
Daher verwenden URLwir beim Empfang eines Bildes die Eigenschaft imageURL aus der URL- Klasse :



Und das ist alles, was Sie tun müssen, um AUSSERHALB etwas in der Sammlung zu akzeptieren Collection View.

Lassen Sie es uns in Aktion sehen. Wir starten gleichzeitig in einem Multitasking-Modus unsere Demo-Anwendung ImageGalleryund Safarimit einer Suchmaschine Google. In Googlesuchen wir nach Bildern zum Thema "Dawn" (Sonnenaufgang). In Safaribereits gebautDrag & DropDer Mechanismus, damit wir eines dieser Bilder auswählen, lange halten, ein wenig verschieben und in unsere Bildergalerie ziehen können.



Das Vorhandensein eines grünen Pluszeichens "+" zeigt an, dass unsere Anwendung bereit ist, ein Bild eines Drittanbieters zu akzeptieren und es an dem vom Benutzer angegebenen Ort in Ihre Sammlung zu kopieren. Nachdem wir es „zurückgesetzt“ haben, dauert es einige Zeit, bis das Bild heruntergeladen ist. Zu diesem Zeitpunkt funktioniert es Placeholder:



Nach Abschluss des Downloads wird das Bild „Zurücksetzen“ an der richtigen Stelle platziert und Placeholderverschwindet:



Wir können die Bilder weiterhin „zurücksetzen“ und in unserem Bild ablegen Sammlungen von noch mehr Bildern:



Nach dem "Zurücksetzen" Placeholder:



Infolgedessen wird unsere Bildergalerie mit neuen Bildern gefüllt:



Nun , da klar ist , dass wir Fotos machen können von außen, wir brauchen nicht das Testbild, und wir werden entfernen:



Unsere viewDidLoad wird sehr einfach: es ist , dass wir unser tun Controller Dragund Dropdelegieren und fügen Erkenner Geste Prise , die die Anzahl der Bilder pro Zeile reguliert:



Natürlich können wir einen Cache für imageCache- Bilder hinzufügen :



Wir füllen imageCache beim "Zurücksetzen" Dropin der performDrop- Methode ...



und beim Abrufen aus dem "Netzwerk" in der benutzerdefinierten ImageCollectionViewCell- Klasse :



Und wir verwenden den imageCache- Cache beim Abspielen einer ZelleZelle unserer Bildergalerie in der benutzerdefinierten Klasse ImageCollectionViewCell :



Jetzt beginnen wir mit einer leeren Sammlung ...



... und legen dann ein neues Bild in unserer Sammlung ab ...



... das Bild wird hochgeladen undPlaceholderfunktioniert ...



... und das Bild erscheint an der richtigen Stelle:



Wir füllen unsere Sammlung weiterhin AUSSEN:



Kommt Das Laden von BildernPlaceholdersfunktioniert ...



Und die Bilder erscheinen an der richtigen Stelle:



Wir können also viel mit unserer Bildergalerie tun: Füllen Sie sie AUSSEN, organisieren Sie Elemente INNEN neu, teilen Sie Bilder mit anderen Anwendungen Niyami.
Wir müssen ihr nur beibringen, wie man unnötige Bilder durch „Zurücksetzen“ beseitigtDropim "Mülleimer" in der Navigationsleiste rechts. Wie im Abschnitt „Funktionen der Demoanwendung für Bildergalerien“ beschrieben, wird der „Papierkorb“ durch die GabageView- Klasse dargestellt , die von UIView erbt, und wir müssen ihm beibringen, Bilder aus unserer Sammlung zu akzeptieren ollection View.

Setzen Sie die DropBilder der Galerie in den Papierkorb zurück.


Sofort vom Ort - zum Steinbruch. Ich will hinzufügen GabageView „Interaktion“ Interaktion und es wird UIDropInteraction , da ich versuche zu „reset“ ein Droppaar Dinge. Alle müssen wir sicherstellen , dass UIDropInteraction , dieser Delegat die Delegierten , und ich werde selbst ernennen, die sich selbst , dieser Delegat der Delegierte :



Natürlich unsere Klasse GabageView bestätigen , dass wir ein Protokoll implementieren UIDropInteractionDelegate :



Alles , was wir tun müssen, um damit es funktioniert Drop, Dies dient zur Implementierung der uns bereits bekannten canHandle- Methoden .sessionDidUpdate und performDrop .



Im Gegensatz zu ähnlichen Methoden für die SammlungCollection Viewverfügen wir jedoch nicht über zusätzliche Informationen in Form eines Indexpfads der Deponie.

Lassen Sie uns diese Methoden implementieren.
Innerhalb der Methode canHandle nur die „Drag and Drop“ verarbeitet werdenDrag, die das Bild repräsentieren ein UIImage . Daher werde ichnur true zurückgeben, wenn session.canLoadObjects (ofClass: UIImage.self) :



In der canHandle- Methodesagen Sie im Wesentlichen nur, wenn das "ziehbare" Objekt kein UIImage- Bild istDann macht es keinen Sinn, das "Zurücksetzen" fortzusetzen und nachfolgende Methoden aufzurufen.
Wenn das "ziehbare" Objekt ein UIImage- Image ist , führen wir die sessionDidUpdate- Methode aus . Bei dieser Methode müssen wir lediglich unser UIDropProposal- Angebot zum Zurücksetzen zurückgeben Drop. Und ich bin bereit, nur das "gezogene" LOKALE Objekt des UIImage-Typs des Bildes zu akzeptieren , das Dropüberall in meiner GarbageView "abgelegt" werden kann . Mein GarbageView interagiert nicht mit Bildern, die AUSSEN ausgegeben werden. Ich analysiere also mit der Variablen session.localDragSessionob es ein lokales "Zurücksetzen" Dropgibt und ich den "Zurücksetzen" -Satz in Form des UIDropProposal- Konstruktors mit dem Operationsargument zurücknehme , das den Wert .copy annimmt , da IMMER LOKALES "Ziehen und Ablegen" Dragin meiner Anwendung aus der Sammlung stammt Collection View. Wenn "Drag Drag& Drop" und "Zurücksetzen" DropAUSSEN auftreten, gebe ich den Satz "Zurücksetzen" in Form des UIDropProposal- Konstruktors mit dem Operationsargument zurück , das den Wert .fobbiden annimmt, dh "verboten", und wir erhalten ein Verbotszeichen "Zurücksetzen" anstelle des grünen Pluszeichens .



Kopieren eines UIImage- BildesWir simulieren eine Verringerung der Skalierung auf fast 0, und wenn das "Zurücksetzen" erfolgt, entfernen wir dieses Bild aus der Sammlung Collection View.
Um die Illusion zu erzeugen, dass Bilder für den Benutzer in der "Mülltonne" "abgeladen und verschwunden" werden, verwenden wir die für uns neue VorschauForDropping- Methode , mit der wir das "Dumping" Dropan einen anderen Ort umleiten und gleichzeitig das "abgelegte" Objekt während der Animation transformieren können:



Bei dieser Methode erhalten wir mithilfe des UIDragPreviewTarget- Initialisierers eine neue Vorschau für das zu löschende Zielobjekt und leiten es mithilfe der retargetedPreview- Methode uman einen neuen Ort, an die „Mülltonne“, deren Skalierung auf fast Null reduziert ist :



Wenn der Benutzer einen Finger nach oben hebt, erfolgt ein „Zurücksetzen“ Drop, und ich (wie GarbageView ) erhalte eine performDrop- Nachricht . In der performDrop- Nachricht führen wir den eigentlichen „Reset“ durch Drop. Ehrlich gesagt interessiert uns das Bild, das auf GarbageView selbst abgelegt wurde , nicht mehr, da wir es praktisch unsichtbar machen werden. Höchstwahrscheinlich Dropsignalisiert die Tatsache, dass der „Reset“ abgeschlossen ist, dass wir dieses Bild aus der Sammlung entfernen Collection View. Um dies zu erreichen, müssen wir die Sammlung selbst und den Sammlungsindexpfad kennenverworfenes Bild darin. Woher können wir sie bekommen?

Da der Prozess Drag & Dropin einer einzigen Anwendung kommt, ist es uns zur Verfügung alle lokalen: lokale DragSitzung localDragSession unsere DropSitzung der Sitzung , der lokale Kontext localContext , die unsere Sammlung ist sollectionView und lokale Objekt localObject , die wir selbst zurückgesetzt Bild ist tun Bild von „Galerie“ oder indexPath . Aus diesem Grunde können wir in der Methode bekommen performDrop Klasse GarbageView Sammlung Sammlung , und deren Verwendungdatasource wie ImageGalleryCollectionViewController und Modell Bildergalerie unsereController, können wir eine Reihe von Bildern erhalten Bilder TYPE [ImageModel]:



Mit Hilfe der lokalenDragSitzung localDragSession unsererDropSitzung Session konnten wir alle die „ziehen“ bekommen auf GarbageView Drag Elemente Elemente , und es kann eine Menge sein, wie wir wissen, und Alle von ihnen sind Bilder unserer collectionView-Sammlung . ErstellenDragElementen dragItems unserer SammlungCollection Viewhaben wir für jeden „drag“ vorgesehenDragElementdragItem lokales Objekt localObject , der das Bild ist Bild , aber es istwir nicht beiinternen Reorganisation Sammlung in handlichen kommen Collection , aber die „Reset“ Bildergalerie „Abfalleimer“ wir in den lokalen Einrichtung dringend brauchen localObject „drag“ Objekt dragItem , nach dieserZeit Wir haben keinen Koordinator , der so großzügig Informationen darüber austauscht, was in der collectionView- Sammlung geschieht. Deshalb wollen wir das lokale Objekt localObject Index war indexPath in der BildArray - Bilder unserer ModelleimageGallery . Nehmen Sie die notwendigen Änderungen der Methode dragItems (bei indexPath: IndexPath) Klasse ImageGalleryCollectionViewController :



Jetzt können wir alle „pretaskivaemogo“ Element nehmen Element es localObject , die der Index indexPath in der BildArray - Bilder unserer Modelle Bildergalerie , und an die ArrayIndizes senden Indizes und Array indexPahes delete Bilder:



die Indexarray Wissen Indizes und Array indexPahes delete Bilder in den Verfahren performBatchUpdatesSammlung Sammlung wir alle gelöschten Bilder von Models entfernen Bilder und aus der Sammlung der Sammlung :



Führen Sie die Anwendung, füllen Sie die Galerie mit neuen Bildern:



ein Paar von Bildern auswählen , die wir von unserer Galerie entfernen möchten ...



... „werfen“ , um sie auf das Symbol mit der „Mülltonne“ ...



Sie reduziert fast auf 0 ...



... und verschwinden aus der Sammlung Collection View, versteckt im "Mülleimer":



Speichern von Bildern zwischen den Starts.



Um die Bildergalerie zwischen den Läufen zu speichern, verwenden wir UserDefaults , nachdem wir unser Modell in ein JSONFormat konvertiert haben. Um dies zu tun, wird fügen wir unseren ControllerVariable var defailts ...



... und im Modell Bilddatenbank und ImageModel Protokoll Kodierbare :



String String Arrays von der Array , der URL und Doppel setzen bereits das Protokoll Kodierbare , so dass wir an die Arbeit Codierung nicht zu bekommen , sonst haben , etwas zu tun und Dekodieren in ImageGallery- Modelle im JSONFormat.
Wie bekommen wir die JSONVersion von ImageGallery ?
Erstellen Sie dazu eine berechnete Variable var json , die das Ergebnis eines Versuchs zurückgibt, sich selbst mit JSONEncoder.encode () in das folgende JSONFormat zu konvertieren:



Und das ist alles. Entweder werden Daten als Ergebnis der Konvertierung von self in format zurückgegeben JSON, oder nil, wenn diese Konvertierung fehlschlägt, obwohl letzteres niemals geschieht, da dieser TYP zu 100% codierbar ist . Die optionale Variable json wird nur aus Symmetriegründen verwendet.
Jetzt haben wir eine Art und Weise Modelle zu konvertieren Bilddatenbank in Daten - Format JSON. Hat die json- Variable TYPE- Daten? was in UserDefaults gespeichert werden kann .
Stellen Sie sich nun vor, wir haben es irgendwie geschafft, die JSON-JSON Daten abzurufen , und ich möchte daraus unser Modell, eine Instanz der ImageGallery- Struktur, neu erstellen . Für diese sehr leicht zu schreiben initializer Bildergalerie sind die Eingabeargumente die Daten der json . Dieser Initialisierer ist ein "fallender" Initialisierer (JSONfailable) Wenn es nicht initialisiert wird, es „fällt“ und kehrt nil :



Ich bin nur eine neuer Wert bekomme newValue von den Decodern JSONDecoder und versuche , die Daten zu dekodieren die json , die zu meinen initializer übertragen werden, und dann ordnet sie das Selbst .
Wenn ich das geschafft habe, bekomme ich eine neue Instanz von ImageGallery , aber wenn mein Versuch fehlschlägt, gebe ich null zurück , da meine Initialisierung "fehlgeschlagen" ist.
Ich muss sagen, dass wir hier viel mehr Gründe haben, um zu „scheitern“ ( fail), weil es durchaus möglich ist, dass die json-JSON Datenkann verwöhnt oder leer sein, all dies kann zum "Fall" ( fail) des Initialisierers führen.

Jetzt können wir die READ implementieren JSONDaten und Wiederherstellungsmodell Bildergalerie die Methode viewWillAppear unsere Controller...



... sowie ein Eintrag im Beobachter didSet {} Eigenschaften Bildergalerie :



Lassen Sie uns die Anwendung ausführen und füllen unsere Galerie von Bildern:



Wenn wir die Anwendung zu schließen und wieder öffnen, können wir unsere bisherigen Galerie sehen In UserDefaults gespeicherte Bilder .

Fazit


Dieser Artikel zeigt am Beispiel einer sehr einfachen Demo-Anwendung „Image Gallery“, wie einfach es ist, Technologie Drag & Dropin eine iOSAnwendung zu integrieren . Dies ermöglichte es, die Bildergalerie vollständig zu bearbeiten, neue Bilder aus anderen Anwendungen dort zu „werfen“, vorhandene zu verschieben und unnötige zu löschen. Und auch, um in der Galerie gesammelte Bilder an andere Anwendungen zu verteilen.

Natürlich möchten wir viele solcher thematischen malerischen Bildersammlungen erstellen und direkt auf dem iPad oder iCloud Drive speichern. Dies kann erfolgen, wenn jede solche Galerie als permanent gespeichertes UIDocument interpretiert wird. Eine solche Interpretation ermöglicht es uns, zur nächsten Abstraktionsebene aufzusteigen und eine Anwendung zu erstellen, die mit Dokumenten arbeitet. In einer solchen Anwendung werden Ihre Dokumente von der DocumentBrowserViewController- Komponente angezeigt, die der Anwendung sehr ähnlich ist Files. Auf diese Weise können Sie UIDocument- Bilder vom Typ „Bildergalerie“ sowohl auf Ihrem als auch auf Ihrem Bildschirm erstellen iPadund iCloud Drivedas gewünschte Dokument zum Anzeigen und Bearbeiten auswählen.
Dies ist jedoch das Thema des nächsten Artikels.

PS Der Code der Demo-Anwendung vor Drag & Dropund nach der Implementierung des Mechanismus befindet sich auf Github .


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


All Articles