Drag & Drop in Ihren iOS-Apps

  • Tutorial


Der Mechanismus Drag & Dropin iOS 11und iOS 12ist eine Möglichkeit, Daten asynchron in einer Anwendung und zwischen verschiedenen Anwendungen asynchron zu kopieren oder zu verschieben. Obwohl diese Technologie etwa 30 Jahre alt ist, ist sie buchstäblich zu einer „bahnbrechenden“ Technologie geworden iOS, da Sie zwar etwas hineinziehen iOS, multitouchaber mit dem Rest des Systems frei interagieren und Daten zum Zurücksetzen aus verschiedenen Anwendungen sammeln können.

iOSermöglicht das gleichzeitige Erfassen mehrerer Objekte. Sie müssen für die Auswahl nicht leicht zugänglich sein: Sie können das erste Objekt nehmen, dann zu einer anderen Anwendung gehen und sich etwas anderes holen - alle Objekte werden in einem „Stapel“ unter Ihrem Finger gesammelt. Rufen Sie dann das Universaldock auf dem Bildschirm auf, öffnen Sie eine beliebige Anwendung und erfassen Sie das dritte Objekt. Gehen Sie dann zum Bildschirm mit laufenden Anwendungen, und legen Sie die Objekte ohne Freigabe der Objekte in einem der geöffneten Programme ab. Eine solche Freiheit zu der Aktion verfügbar iPadauf iPhoneBerichterstattung Drag & Dropin den iOSbegrenzten Anwendungsbereich.

Die beliebtesten Apps ( Safary, Chrome, IbisPaint X, Mail, Photos, Files, etc.) sind bereits eingebauten Mechanismus Drag & Drop. Darüber hinausAppleDas macht es Entwicklern sehr einfach und intuitiv API, einen Mechanismus Drag & Dropin Ihre Anwendung einzubetten . Der Mechanismus Drag & Dropfunktioniert genau wie Gesten für UIView und verwendet das Konzept von „Interaktionen“ Interaktionen , die ein bisschen wie Gesten sind, sodass Sie sich den Mechanismus Drag & Dropeinfach als eine wirklich mächtige Geste vorstellen können .

Es ist ebenso wie Gesten sehr einfach in Ihre Anwendung zu integrieren. Insbesondere, wenn Ihre Anwendung eine UITableView- Tabelle oder eine UICollectionView- Auflistung verwendet , da diese API Drag & Dropverbessert und auf eine höhere Abstraktionsebene angehoben wurde, da die Auflistung Collection ViewIhnen bei indexPath hilftSammlungselement, das Sie ziehen möchten Drag. Sie weiß, wo sich Ihr Finger befindet, und interpretiert ihn als indexPath eines Sammlungselements, das Sie gerade ziehen Drag, oder als indexPath eines Sammlungselements, wo Sie Dropetwas ablegen . Die Collection Collection Viewstellt Ihnen also indexPath zur Verfügung , aber ansonsten ist es absolut genauso API Drag & Dropwie bei einer normalen UIView .

Prozess Drag & Dropzur iOShat 4 verschiedene Phasen:

Heben


Anheben ist, wenn der Benutzer eine lange Druckbewegung ausführt , um ein Element anzuzeigen, das „gezogen und abgelegt“ wird. In diesem Moment wird eine sehr leichte so genannte "Vorschau" ( lift preview) des angegebenen Elements gebildet, und der Benutzer beginnt dann, Draggingseine Finger zu bewegen .



Ziehen (ziehen)


Ziehen (Ziehen) - Dies ist, wenn der Benutzer das Objekt auf der Bildschirmoberfläche bewegt. In dieser Phase kann eine "Vorschau" ( lift preview) für dieses Objekt geändert werden (ein grünes Pluszeichen "+" oder ein anderes Zeichen erscheint) ...



... auch einige Interaktionen mit dem System sind zulässig: Sie können auf ein anderes Objekt klicken und es der aktuellen Sitzung hinzufügen. " Drag and Drop:



Fallen lassen


Ein Abfall tritt auf, wenn der Benutzer einen Finger anhebt. An diesem Punkt können zwei Dinge passieren: Entweder wird das DragObjekt zerstört oder das Objekt wird Dropam Ziel „ausgegeben“ .



Datenübertragung


Wenn der Prozess der „Ziehen“ Ziehen widerrufen und hielt nicht ein „Reset“ Tropfen , was geschieht , Datenübertragung (Datenübertragung) , an dem der „reset Punkt“ fordert Daten von der „Quelle“, und es gibt die asynchrone Datenübertragung.

In diesem Lernprogramm zeigen wir anhand der Picture Gallery- Demoanwendung , die den Stanford CS193P-Kursen von den Hausaufgaben entlehnt wurde , wie einfach es ist, einen Mechanismus Drag & Dropin Ihre iOSAnwendung einzubetten .
Wir werden der Sammlung die Collection View Möglichkeit geben, sich mit Bildern von außen zu füllen und die Elemente, die sich INSIDE selbst befinden, mithilfe des Mechanismus neu zu ordnenDrag & Drop. Darüber hinaus wird dieser Mechanismus verwendet, um nicht benötigte Elemente der Sammlung Collection Viewim "Mülleimer" zurückzusetzen, einem normalen UIView, der durch eine Schaltfläche in der Navigationsleiste dargestellt wird. Mit dem Mechanismus können wir auch Drag & DropBilder freigeben, die in unserer Galerie aus anderen Anwendungen gesammelt wurden, z. B. mit „Notizen“ ( Notesoder Notability) oder per E-Mail Mailoder mit einer Fotobibliothek ( Photo).

Bevor Drag & Dropich mich jedoch auf die Implementierung des Mechanismus in der Demo-Anwendung „Gallery of Images“ konzentriert, werde ich kurz auf die wichtigsten Komponenten eingehen.

Funktionen der Demo-Anwendung "Image Gallery"


Die Benutzeroberfläche ( UI) der Bildergalerie-Anwendung ist sehr einfach. Dieses "Bildschirmausschnitt" wurde Image Gallery Collection View Controllereingefügt in Navigation Controller: Der



zentrale Teil der Anwendung wird Image Gallery Collection View Controllervon der ImageGalleryCollectionViewController- Klasse mit dem Image Gallery-Modell als Variable unbedingt unterstützt. Var imageGallery = ImageGallery () :



Das Modell wird durch eine Bildgallery-Struktur dargestellt, die ein Array von Bildern enthält , in denen jedes Bild durch eine Struktur beschrieben wird struct ImageModel, das die URLURL der Bildposition (das Bild selbst wird nicht gespeichert) und das Seitenverhältnis enthältaspectratio :



Unsere ImageGalleryCollectionViewController implementiert die Datasource - Protokoll:



Benutzerdefinierte Zellsammlung Zelle enthält ein Bild Imageview: UIImageView! und Spinneraktivitätsindikator: UIActivityIndicatorView! und unterstützt durch den Benutzersubclass ImageCollectionViewCell Klasse UICollectionViewCell :



Public APIKlasse ImageCollectionViewCell - diesesURLBild imageURL . Sobald wir es installieren, wird esUIaktualisiert, dh die Daten für das Image in dieser imageURL werden asynchron ausgewählt .und in einer Zelle angezeigt. Während Daten aus dem Netzwerk abgerufen werden, funktioniert der Spinner- Aktivitätsindikator. Dies zeigt an , dass wir gerade Daten abrufen.

Ich verwende eine URLglobale globale Warteschlange (qos: .userInitiated) , um Daten für eine bestimmte Warteschlange mit dem Quality of Service-Argument  qos abzurufen , das auf .userInitiated gesetzt ist , da ich Daten auf Anforderung des Benutzers auswähle:



Jedes Mal, wenn Sie Ihre eigenen Variablen innerhalb der Schließung verwenden, In unserem Fall handelt es sich um imageView und imageURL , der Compiler zwingt Sie, sich selbst einzustellen . Sie fragen sich also: "Gibt es nicht einen" Zirkelverweis auf das Gedächtnis "(memory cycle)? ”Wir haben hier keine explizite„ zyklische Speicherreferenz “( memory cycle), weil das Selbst selbst keinen Zeiger auf diese Schließung hat.

Im Falle von Multithreading müssen Sie jedoch berücksichtigen, dass die Zellen in der Auflistung Collection Viewaufgrund der Methode dequeueReusableCell wiederverwendbar sind . Jedes Mal, wenn eine Zelle (neu oder wiederverwendet) auf dem Bildschirm erscheint, wird das Bild asynchron vom Netzwerk aus gestartet (der Spinner der Spinneraktivitätsanzeige dreht sich zu diesem Zeitpunkt ).

Sobald der Download abgeschlossen ist und das Bild empfangen wurde, erfolgt eine Aktualisierung.UI diese Zellsammlung. Wir warten jedoch nicht auf das Laden des Bildes. Wir scrollen weiter durch die Sammlung und die von Ihnen bemerkte Sammlungszelle geht vom Bildschirm aus, ohne die eigene zu aktualisieren UI. Es sollte jedoch ein neues Bild von unten angezeigt werden, und dieselbe Zelle, die den Bildschirm verlassen hat, wird wiederverwendet, jedoch für ein anderes Bild, das möglicherweise schnell geladen und aktualisiert wird UI. Zu diesem Zeitpunkt kehrt das zuvor in dieser Zelle gestartete Bild zurück und der Bildschirm wird aktualisiert, was zu einem falschen Ergebnis führt. Dies liegt daran, dass wir verschiedene Dinge ausführen, die in verschiedenen Threads im Netzwerk arbeiten. Sie kommen zu verschiedenen Zeiten zurück.

Wie können wir die Situation beheben?
Im Rahmen des von uns verwendeten GCD- Mechanismus Das Laden eines Bildes einer Zelle, die den Bildschirm verlassen hat, kann nicht abgebrochen werden. Wenn jedoch unsere imageData- Daten aus dem Netzwerk stammen , überprüfen Sie die URLURL , die zum Laden der Daten führte, und vergleichen Sie sie mit dem, was der Benutzer im Moment in dieser Zelle haben möchte. da ist imageURL . Wenn sie nicht übereinstimmen, werden wir die UIZelle nicht aktualisieren und auf die benötigten Bilddaten warten:



Diese scheinbar absurde Codezeile url == self.imageURL sorgt dafür, dass in einer Multithread-Umgebung alles ordnungsgemäß funktioniert, die nicht dem Standard entspricht. Tatsache ist, dass einige Dinge bei der Multithread-Programmierung in einer anderen Reihenfolge als der geschriebene Code auftreten.

Wenn die Bilddaten nicht abgetastet werden konnten, wird ein Bild mit einer Fehlermeldung in Form einer Zeichenfolge „Error“ und einem Emoji mit einem Stirnrunzeln generiert. Nur der leere Bereich in unserer Sammlung Collection Viewkann den Benutzer ein wenig verwirren:



Wir möchten nicht, dass das Bild mit einer Fehlermeldung das Seitenverhältnis dieses fehlerhaften Bildes wiederholt , da in diesem Fall der Text zusammen mit dem Emoji gestreckt oder verkleinert wird. Wir möchten, dass es neutral ist - quadratisch, das heißt, das Seitenverhältnis des Seitenverhältnisses liegt nahe bei 1,0.



Diesen Wunsch müssen wir uns mitteilen, Controllerdamit er das Seitenverhältnis in seinem ImageGallery- Modell korrigiert .für den entsprechenden indexPath . Dies ist ein interessantes Problem, es gibt viele Wege, um es zu lösen, und wir werden die einfachste von ihnen wählen - mit dem optionalen Closure ( closure) var changeAspectRatio: (() -> Void)? . Es kann Null sein und muss nicht gesetzt werden, wenn dies nicht erforderlich ist:



Wenn ich die Schließung aufrufe , ändern Sie AspectRatio (). Im Falle eines fehlerhaften Datenmusters verwende ich die optionale Kette . Jeder, der an einer Einstellung interessiert ist, wenn ein fehlerhaftes Bild empfangen wird, kann diese Schließung auf eine bestimmte Einstellung setzen. Und das machen wir in unserer cellForItemAt-Controller Methode :



Details finden Sie hier .

Um Bilder von der korrekten Anzeige aspectratio verwendete Methode sizeForItemAt Delegat UICollectionViewDelegateFlowLayout :



Neben der Sammlung von Bildern Collection Viewauf unserer UIwir uns auf die Navigationsleiste Taste platziert haben Bar Buttonc benutzerdefinierte Bilder GarbageView , mit dem „Papierkorb“ als ein Subview :



Diese Zahl speziell die Hintergrundfarbe für die meisten GarbageView  und Tasten Uibuttonmit dem Bild der „Mülltonne“ (in der Tat gibt es einen transparenten Hintergrund) für Sie , dass ein Benutzer , um zu sehen , die Bildergalerie „Papierkorb“ so viel mehr Handlungsspielraum , wenn der „Reset“ „wirft“  Tropfen , als nur Mülleimer-Symbol.
In Klasse GarbageView  zwei Initialisierer und beide verwenden die Methode Setup () :



In dem Verfahren  Setup () I auch als hinzugefügt   subview Taste myButton mit dem Bild der „Mülleimer“, aus dem Standard genommen Bar ButtonTasten  Trash:



I einen transparenten Hintergrund für Set GarbageView :



„Garbage kann“ Größe und Lage des Ortes wird durch die Methode bestimmt werden layoutSubviews () der Klasse UIView , abhängig von den Grenzen Grenzen von UIView :



Dies ist die erste Version der Beispielanwendung „Bildergalerie“ liegt in der Region GithubOrdner ImageGallery_beginning. Wenn Sie diese Version der Anwendung "Bildergalerie" starten, sehen Sie das Ergebnis der Arbeit der Anwendung an den Testdaten, die wir anschließend löschen und nur in der Bildergalerie ausfüllen. EXTERN:



Der Plan für die Einführung des Mechanismus Drag & Dropin unsere Anwendung sieht wie folgt aus:

  1. Zunächst geben wir unserer Bildersammlung die Collection ViewMöglichkeit, UIImage-Drag Bilder sowohl extern als auch lokal "zu ziehen".
  2. dann werden wir unsere Bildersammlung beibringen Collection View, UIImage- Bilder Dragvon außen oder lokal „gebunden“ zu machen ,
  3. Wir lehren auch unsere GarbageView  Taste „Mülltonne“ nehmen zu „ziehen“ von einer lokalen Sammlung von Collection ViewBildern UIImage und entfernen Sie sie aus der SammlungCollection View


Wenn Sie bis zum Ende dieses Lernprogramms durchgehen und alle erforderlichen Codeänderungen vornehmen, erhalten Sie die endgültige Version der Demoanwendung „Image Gallery“, die über einen Mechanismus verfügt Drag & Drop. Sie ist Githubin dem Ordner eingeschaltet ImageGallery_finished.

Die Effizienz des Mechanismus Drag & Dropin Ihrer Sammlung wird Collection Viewdurch zwei neue Delegierte sichergestellt.
Die Methoden des ersten Delegaten, dragDelegate , sind so konfiguriert, dass Drag-and-Drop-Einstellungen initialisiert und angepasst werdenDrags .
Die Methoden des zweiten Delegaten, dropDelegate , führen das "Ziehen Dragsund Ablegen" aus und bieten im Wesentlichen "Datentransfer ( Data transfer)" und benutzerdefinierte Animationen beim "Zurücksetzen".Dropsowie andere ähnliche Dinge.

Es ist wichtig zu wissen, dass beide Protokolle völlig unabhängig sind. Sie können das eine oder andere Protokoll verwenden, wenn Sie nur "Überdrehen" Dragoder nur "Dumping" benötigen Drop, Sie können jedoch beide Protokolle gleichzeitig verwenden und gleichzeitig "Ziehen" Dragund "Zurücksetzen" ausführen Drop, wodurch zusätzliche Funktionen Drag & Dropfür den Neuordnungsmechanismus verfügbar werden Artikel in Ihrer Sammlung Collection View.

Drag & Drop von DragElementen aus der SammlungCollection View


Das Implementieren eines DragProtokolls ist sehr einfach. Als erstes sollten Sie sich selbst als DragDelegate- Delegat festlegen :



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



Sobald wir dies tun, startet der Compiler zu „beschweren“, wir auf den roten Kreis klicken und wir werden gefragt: „möchten Sie die erforderlichen Methoden des Protokolls hinzugefügt werden soll UICollectionViewDragDelegate ?“
ich antworte: „natürlich will ich das !“ , und klicken Sie auf die Schaltfläche Fix:



die einzige von Protokoll erforderlich UICollectionViewDragDelegateist die itemsForBeginning- Methode , die dem DragSystem mitteilt , WAS wir "ziehen und ablegen ". Verfahren itemsForBeginning aufgerufen , wenn der Benutzer auf „drag and drop“ beginnt ( Dragging) Zellsammelzelle .

Beachten Sie, dass die Auflistung Collection Viewder Methode indexPath hinzugefügt hat . Dies sagt uns, welches Element der Sammlung, welcher indexPath wir "ziehen". Dies ist sehr praktisch für uns, da es Aufgabe der Anwendung ist, die Verwendung der Argumente session und indexPath zu übernehmen, um herauszufinden , wie mit Drag & Drop umgegangen wirdDrag .

Wenn ein Array zurückgegeben wird[UIDragItems] der " overstrightened " -Elemente, das "overstrightening" wirdDraginitialisiert, aber wenn das leere Array [] zurückgegeben wird, wird das "Überladen"Dragignoriert.

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



Wie sieht ein Drag & Drop- UIDragItem aus ?
Es gibt 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 “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 beispielsweise eine kostspielige Operation ist. Dies kann ein Bild sein oder etwas, das das Herunterladen von Daten aus dem Internet erfordert. Bemerkenswerterweise ist die Operation Drag & Dropvollständig asynchron. Wenn Sie mit dem Ziehen beginnen Drag, ist es wirklich ein sehr leichtes Objekt ( lift preview). Sie ziehen es überall hin und nichts passiert während des Ziehens. Sobald Sie DropIhr Objekt jedoch irgendwo "werfen" , ist es itemProvider, sollte Ihr "gezogenes" und "fallengelassenes" Objekt wirklich mit realen Daten versorgen, auch wenn dies eine gewisse Zeit in Anspruch nimmt.

Glücklicherweise gibt es viele eingebaute itemProviders . Hierbei handelt es sich um Klassen, die bereits in itemPoviders vorhanden sind iOSund beispielsweise Text ohne Schriftarten ziehen können. Natürlich ist dies ein UIImage- Image . Sie können UIImages überall auswählen und ziehen . NSURL- Klasse , was absolut wunderbar ist. Sie können auf die Seite gehen, auswählen und sie "werfen", wo immer Sie möchten. Dies kann ein Link zu einem Artikel oder seinNSStringWebURLURLfür das Bild, wie es in unserer Demo sein wird. Diese Farbklassen UIColor , Kartenelement MKMapItem kontaktieren CNContact aus dem Adressbuch, eine Menge Dinge , können Sie wählen , und „Drag and Drop“. Alle sind itemProviders .

Wir ziehen ein UIImage- Bild . Es befindet sich in der Erfassungszelle Collection Viewmit dem indexPath , was mir hilft, die Zelle auszuwählen , OutletimageView aus der Zelle herauszuholen und ihr Bild abzurufen .

Lassen Sie uns diese Idee in ein paar Zeilen Code ausdrücken.
Zuerst fordere ich meine Sammlung Collection Viewüber Zellen anZelle für ein Element Punkt , zu dem entsprechenden indexPath .



Die cellForItem- Methode (at: IndexPath) für die AuflistungCollection Viewfunktioniert nur für visible (visible) - Zellen. In unserem Fall funktioniert sie jedoch natürlich, weil ichDragdas Auflistungselement auf den Bildschirm"ziehe"und es sichtbar ist.

Also erhielt ich eine "ziehbare" Zelle .
Als nächstes benutze ich das als Operator ? zu dieser Zelle, so dass es den TYP meines Benutzers hatsubclass. Und wenn es funktioniert, bekomme ich eineOutlet imageView , von der ich ihr Image nehme. Ich habe gerade das Bild aufgenommen.für diesen indexPath .

Jetzt, da ich ein Bild habe , muss ich nur eines dieser UIDragItems erstellen , wobei das resultierende Bild als itemProvider verwendet wird , d. H. Die Sache, die uns Daten liefert.
Ich kann dragItem mit dem UIDragItem- Konstruktor erstellen , der das itemProvider- Argument übernimmt :



Dann erstellen wir den itemProvider für das Image- Image ebenfalls unter Verwendung des NSItemProvider- Konstruktors . Es gibt mehrere Konstruktoren fürNSItemProvider , aber es gibt einen wirklich bemerkenswerten unter ihnen - NSItemProvider (Objekt: NSItemProviderWriting) :Sie geben



diesem NSItemProvider- Konstruktoreinfach ein Objekt , und er kann einen itemProvider daraus machen. Als ein solches Objekt gebe ich dem Bild das Bild , das ich von der Zelle der Zelle erhalten habe, und erhalte den itemProvider für den UIImage .
Und das ist alles. Wir haben ein DragItem erstellt und sollten es als Array mit einem Element zurückgeben.

Aber bevor ich das DragItem zurückschicke, Ich werde noch etwas tun, nämlich die localObject- Variable für dragItem gleich dem resultierenden Bild setzen .



Was bedeutet das?
Wenn Sie Draglokal ziehen und ablegen , dh innerhalb Ihrer Anwendung, müssen Sie nicht all diesen mit itemProvider verknüpften Code durch asynchronen Datenabruf durchgehen. Sie müssen dies nicht tun, Sie müssen nur localObject nehmen und verwenden. Dies ist eine Art "Kurzschluss" mit lokalem "Drag and Drop" Drag.

Der Code, den wir schreiben, funktioniert, wenn Sie Dragüber unsere Sammlung hinaus ziehen.Collection Viewzu anderen Anwendungen, aber wenn wir Draglokal ziehen und ablegen , können wir localObject verwenden . Als nächstes gebe ich ein Array zurück, das aus einem einzigen Element von dragItem besteht .

Übrigens, wenn ich aus irgendeinem Grund nicht bekommen konnte Bild für diese Zelle Zelle , kehre ich leeres Array [] , bedeutet dies , 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 handelt es sich um eine collectionView- Sammlung , die sich später als nützlich erweisen wird:



Wenn Sie mit dem Ziehen beginnen Drag, können Sie dem Ziehen weitere Elemente hinzufügen , indem Sie einfach eine Tippen- Geste darauf ausführen . Daher können Sie Dragviele Objekte gleichzeitig ziehen. Und es ist einfach eine andere Methode delegieren zu implementieren mit UICollectionViewDragDelegate , sehr ähnlich dem Verfahren itemsForVeginning , ein Verfahren namens itemsForAddingTo . Die itemsForAddingTo- Methode sieht genauso aus wie die itemsForVeginning- Methode und gibt genau das gleiche zurück, da sie uns auch den indexPath von dem gibt, was der Benutzer während des Drag & Drop-Vorgangs " angetippt " hat.DragUnd ich bekomme ganz das Bild Bild der Zelle , in der „tapnul“ user, und es zurückgeben.



Die Rückgabe eines leeren [] - Arrays aus der itemsForAddingTo- Methode bewirkt , dass die Tap- Geste wie üblich interpretiert wird, d. H. Als Auswahl dieser Zelle .
Und das ist alles, was wir zum Ziehen und Ablegen brauchen Drag.
Führen Sie die Anwendung aus.
Ich wähle das "Venedig" -Bild aus, halte es für eine Weile und fange an, mich zu bewegen ...



... und wir können dieses Bild tatsächlich in die Anwendung ziehen Photos, da in der oberen linken Ecke des "Drag and Drop" -Bilds ein grünes Pluszeichen "+" erscheint. Ich kann eine Tippgeste ausführenein weiteres Bild von „Artik“ aus der Sammlung Collection View...



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



Da der PhotosMechanismus bereits in die Anwendung integriert ist Drag & Drop, funktioniert alles gut und es ist cool.
Also muss ich Dragdas DropGalerie-Bild in andere Anwendungen "überzeichnen" und "zurücksetzen". Ich musste in meiner Anwendung nicht viel tun, außer für die Bereitstellung des Bild- Images als Array [UIDragItem] . Dies ist eine der vielen großartigen Funktionen des Mechanismus Drag & Drop- es ist sehr einfach, ihn in beide Richtungen zum Laufen zu bringen.

DropBilder in Sammlung zurücksetzenCollection View


Jetzt müssen wir einen DropTeil für meine Sammlung erstellen Collection View, damit Sie Dropalle "ziehbaren" Bilder innerhalb dieser Sammlung "ablegen" können. Das gezogene Bild kann sowohl von außen als auch direkt aus dieser Sammlung stammen.
Um dies zu tun, tun wir das Gleiche getan zu delegieren dragDelegate , also machen wir selbst, die selbst delegieren dropDelegate in dem Verfahren von dem viewDidLoad :



Auch 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 vor uns erscheinen die obligatorischen Methoden dieses Protokolls. In diesem Fall wird uns gesagt, dass wir die performDrop- Methode implementieren müssen :



Wir müssen dies tun, andernfalls gibt es kein "reset" Drop. In der Tat werde ich die performDrop- Methode zuletzt implementieren , da es eine Reihe anderer empfohlener AppleMethoden gibt, die für das DropTeil implementiert werden müssen . Dies sind canHandle und dropSessionDidUpdate :



Wenn wir diese beiden Methoden implementieren, können wir ein kleines grünes Pluszeichen „+“ erhalten, wenn wir die Bilder AUSSERHALB in unsere Sammlung ziehen Сollection View, und außerdem werden wir nicht versuchen,

das abzugeben , was wir nicht verstehen. Lassen Sie uns canHandle implementieren . Sie haben eine Version der canHandle- Methode , die auf die Auflistung abzieltСollection View . Diese Methode Сollection Viewsieht jedoch genauso aus wie eine ähnliche Methode für eine normale UIView , dort gibt es keinen indexPath . Wir müssen lediglich session.canLoadObjects (ofClass: UIImage.self) zurückgeben , d. h dass ich das "Zurücksetzen" der Objekte dieser Klasse akzeptiere PAS in meiner Sammlung Сollection View:



Dies reicht jedoch nicht aus, um das DropBild in meiner Sammlung Collection View AUSSERHALB "zurückzusetzen" .
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 DropBild „dumping“ jedoch EXTERN ist, sollten wir nur diejenigen „Drag and Drop“ verarbeiten Drag, die das UIImage- Bild zusammen mit URLdiesem Bild darstellen, da die UIImage- Bilder nicht direkt gespeichert werdenim Modell. In diesem Fall werde ich  in der canHandle- Methode  nur dann true zurückgeben , wenn ein Paar von session.canLoadObjects- Bedingungen (ofClass: NSURL.self) && session.canLoadObjects (ofClass: UIImage.self) gleichzeitig ausgeführt wird  & :



es bleibt mir zu prüfen, ob ich mit einem "reset" beschäftigt bin. Von außen oder von innen. Ich werde es tun , mit Hilfe von berechneten Konstanten isself , für die Berechnung von dem ich so etwas in verwenden können  , Dropdie Sitzung der 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 Delegat UICollectionViewDragDelegate :



Ich werde den lokalen Kontext localContext auf Gleichheit mit meiner collectionView untersuchen . True TYPE für localContext ist  Any , und ich mussmit Hilfe des as- Operatorsein "Casting" von TYPE Any vornehmen . UICollectionView :



Wenn der lokale Kontext  (session.localDragSession? .LocalContext als? UICollectionView) mit meiner collectionView übereinstimmt, istdie berechnete Variable isSelf trueund es gibt ein lokales "reset" in meiner Sammlung. Wenn diese Gleichheit verletzt wird, handelt es sich um ein "Reset" DropEXTERNAL.

Die canHandle- Methode berichtet, dass wir diese Art von "Drag and Drop" nur Dragauf unsere Sammlung anwenden können Collection View. Andernfalls macht es überhaupt keinen Sinn, über "Dumping" zu sprechen Drop.

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

Bei diesem Verfahren haben wir die Rückkehr DropVorschlag, die Werte haben kann .copy oder .move oder .cancel oder .forbidden für Argument Betrieb . Und das sind alles die Möglichkeiten, die wir im gewöhnlichen Fall haben, wenn wir mit dem üblichen UIView umgehen .

Die Auflistung Collection Viewgeht jedoch weiter und bietet die Rückgabe der spezialisierten Angebote UICollectionViewDropProposal an , die subclasszur Klasse UIDropProposal gehört und zusätzlich zur Operation operation die Angabe eines zusätzlichen absichtlichen Parameters für die Auflistung ermöglicht Collection View.

ParameterDie Absicht sagt der Sammlung,Collection Viewob das "reset" -Element in einer bereits vorhandenen Zelle platziert werden soll oder ob wir eine neue Zelle hinzufügen möchten . Sehen Sie den Unterschied? Im Falle einer Sammlung müssenCollection Viewwir unsere Absicht mitteilen .

In unserem Fall möchten wir immer eine neue Zelle hinzufügen, damit Sie sehen, was unser Intent- Parameter ist.
Die Wahl des zweiten Konstruktor UICollectionViewDropProposal :



In unserem Fall wollen wir immer eine neue Zelle hinzufügen und Einstellung Absicht  wird Wert auf .insertAtDestinationIndexPath im Gegensatz.insertIntoDestinationIndexPath .

 

Ich habe wieder die berechnete Konstante  isSelf verwendet , und wenn es sich um eine Selbstreorganisation handelt , dann mache ich eine .move- Bewegung, ansonsten mache ich eine Kopie von .copy . In beiden Fällen verwenden wir .insertAtDestinationIndexPath , das heißt, neue Zellen werden in Zellen eingefügt .

Bisher habe ich die performDrop- Methode nicht implementiert, aber werfen wir einen Blick darauf, was die Sammlung bereitsCollection Viewmit dieser kleinen Informationtun kann, die wir ihr zur Verfügung gestellt haben.

Ich ziehe ein BildSafarimit einer Suchmaschine aus Google, und dieses Bild hat ein grünes "+" - Zeichen darauf, was darauf hinweist, dass unsere Bildergalerie bereit ist, dieses Bild nicht nur zu empfangen und zu kopieren URL, sondern auch einen Platz in der Sammlung anzugeben Collection View:



Ich kann ein paar weitere Bilder in Safariund anklicken "Verschobene" Bilder werden bereits 3 sein:



Wenn ich jedoch einen Finger hebe und diese "ablege" Drop, werden sie nicht in unsere Galerie eingefügt , sondern kehren einfach zu ihren früheren Orten 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 absolut wunderbar für den Mechanismus.Drag & Drop, es hat sehr leistungsfähige Funktionen dafür. Wir haben es kaum berührt, nachdem wir 4 Zeilen Code geschrieben hatten, aber die Wahrnehmung von „Reset“ ist bereits weit genug fortgeschritten Drop.
Gehen wir zurück zum Code und implementieren Sie die performDrop- Methode .



In dieser Methode können wir nicht mit 4 Zeilen Code umgehen , da die Methode performDrop etwas komplizierter ist, aber nicht zu kompliziert.
Wenn ein „Reset“ erfolgt Drop, müssen wir in der PerformDrop- Methode unser Model, die ImageGallery-Bildergalerie, mit einer Liste von Bildern und Bildern aktualisieren, und wir müssen unsere Sammlung collectionView aktualisieren .

Wir haben zwei verschiedene "Reset" -Szenarien.Drop.

Wenn in Dropmeiner collectionView ein „Reset“ erfolgt , muss ich das DropObjekt an der neuen Position zurücksetzen und aus dem alten Speicherort entfernen. In diesem Fall verschiebe ( .move ) dieses Element. 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 Element «gezogen» Artikel Daten zu holen.

Wenn wir Dropin der collectionView- Sammlung ein „Reset“ durchführen , wird uns die Sammlung vom Koordinator zur Verfügung gestellt . Die erste und wichtigste Sache, die der Koordinator uns erzähltDer Koordinator ist der destinationIndexPath , dh der Zielpunkt "reset" indexPathDrop , dh der Ort, an dem wir "zurücksetzen" werden.



Aber destinationIndexPath kann gleich Null , so können Sie das „rücksetzbar“ Bild in diesem Teil der Sammlung ziehenCollection View, die zwischen einigen bestehenden Zellen kein Fleck Zellen , so kann es gut sein , null . Wenn es der Fall ist, dann schaffe ich IndexPath mit dem 0ten Element Elemente in dem 0ten Abschnitt des Abschnitts genannt .



Ich könnte jeden anderen Indexpfad wählen, außer diesenIch werde standardmäßig indexPath verwenden .

Jetzt wissen wir, wohin wir gehen sollenDrop. Wir müssen gehen durch alle „blow-off“ Artikel coordinator.items , vom KoordinatorVerfügung gestellt Koordinator . Jedes Element aus dieser Liste hat den UICollectionViewDropItem- TYPEund kann uns sehr interessante Informationen liefern.

Wenn ich beispielsweise den sourceIndexPath von item.sourceIndexPath abrufen kann , weiß ich genau, dass dieses "Ziehen und Ablegen"Dragselbst erstellt und selbst ist. Die Quelle des ZiehensDrag ist das Collection-Element mit indexPathgleich zu sourceIndexPath :



Ich muss in diesem Fall nicht einmal localContext betrachten , um herauszufinden, dass dieses "Ziehen und Ablegen" innerhalb der collectionView- Auflistung durchgeführt wurde . Großartig!

Jetzt kenne ich die sourceIndexPath- Quelle und den Zielpunkt destinationIndexPathDrag & Drop , und die Aufgabe wird trivial. Alles , was ich tun müssen , um das Modell zu aktualisieren , so dass die Quelle und der „Zielpunkt“ umgekehrt werden, und dann die Sammlung aktualisieren 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, denn in diesem Fall Drag & Dropfunktioniert der Mechanismus nicht nur in derselben Anwendung, sondern auch in derselben collectionView- Sammlung , und ich kann mit dem Koordinator alle erforderlichen Informationen abrufen . Lassen Sie sie wissen , dass ein einfacher lokaler Fall:



In diesem Fall muß ich nicht einmal localObject , ich „verstecken“ vor, wenn erstellt dragItem und ich kann jetzt ausleihen Element in der Sammlung „gezogen“ Artikel als item.localObject . Wir brauchen es beim "Zurücksetzen"DropBilder im Papierkorb, der sich in derselben Anwendung befindet, aber nicht dieselbe collectionView- Auflistung ist . Nun sind zwei IndexPathes für mich genug : der Quell- SourceIndexPath und der Zielpunkt destinationIndexPath .

Zuerst bekomme ich imageInfo- Informationen über das Bild an der alten Stelle aus dem Modell und entferne es von dort. Und fügen Sie anschließ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 Kollektionssammlung selbst aktualisieren. Es ist wichtig zu verstehen , dass ich möchte nicht alle Daten in meiner Sammlung zu überlasten Collection mit reload () in der Mitte des „Drag and Drop“ -Verfahren  Drag, weil es die ganze „Welt“ unserer Galerie von Bildern setzt, was sehr schlecht ist, weiß ich nicht. Stattdessen werde ich aufräumen und Artikel einfügen Artikel separat:



ich den Artikel in einer Sammlung gelöscht Kollektion mit sourceIndexPath und ein neues Element in der Sammlung mit einfügen destinationIndexPath .

Es sieht so aus, als ob dieser Code gut funktioniert, aber in Wirklichkeit kann dieser Code Ihre Anwendung "zum Absturz bringen". Der Grund ist, dass Sie zahlreiche Änderungen an Ihrer collectionView- Auflistung vornehmen. In diesem Fall muss jeder Schritt der Auflistungsänderung normalerweise mit dem Model synchronisiert werden, was in unserem Fall nicht beachtet wird, da wir beide Vorgänge gleichzeitig ausführen: Löschen und Einfügen. Infolgedessen befindet sich die collection collectionView irgendwann in einem NICHT mit dem Model synchronisierten Zustand.

Es gibt jedoch eine wirklich coole Methode, um dies zu umgehen. Die Collection collectionView verfügt über eine Methode namens performBatchUpdates , die eine Schließung (closure) und innerhalb dieser Schließung kann ich eine beliebige Anzahl dieser deleteItems , insertItems , moveItems und alles, was ich will, platzieren:



Jetzt werden deleteItems und  insertItems als eine Operation ausgeführt, und es wird nie eine Synchronisation Ihres Modells mit der collectionView fehlen .

Und schließlich das letzte , was wir tun müssen , um den Koordinator fragt Koordinator zu implementieren und animieren selbst „Reset“ Drop:



Sobald Sie Ihren Finger vom Bildschirm nehmen, wird das Bild bewegt, alles in der gleichen Zeit geschieht: „Reset“, um das Verschwinden des Bildes an einem Ort und Aussehen an einem anderen.
Versuchen wir, das Testbild „Venedig“ in unserer Bildergalerie an das Ende der ersten Reihe zu verschieben ...



... und es zurückzusetzen:



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

Lassen Sie uns jetzt NICHT einen lokalen Fall behandeln, das heißt, wenn ein "dumped" -Element AUSSERHALB kommt, das heißt, aus einer anderen Anwendung.
Dazu schreiben wir in Bezug auf sourceIndexPath noch etwas in den Code . Wenn wir nicht über sourceIndexPath verfügen , bedeutet dies, dass das "reset" -Element von irgendwo außerhalb kam und wir die Datenübertragung mit dem itemProver- Dropdown-Element item.dragItem.itemProvider aktivieren müssen :



Wenn Sie Drag etwas EXTERNES ziehen und ablegen und "werfen" Drop, stehen diese Informationen sofort zur Verfügung? Nein, Sie wählen Daten aus der Drag-and-Drop-Funktion ASYNCHRONOUS aus. Was aber, wenn die Probe 10 Sekunden dauert? Was macht die Sammlung zu diesem Zeitpunkt Сollection View? Darüber hinaus werden die Daten möglicherweise nicht in der Reihenfolge angezeigt, in der wir sie angefordert haben. Dies ist gar nicht so einfach, und in diesem Fall wurde Appleeine Сollection Viewvöllig neue Technologie für die Verwendung von Ersatzstoffen vorgeschlagen Placeholders.

Sie platzieren einen Collection View Ersatz in Ihrer Sammlung Placeholder, und die Sammlung Collection Viewverwaltet alles für Sie. Wenn Sie nun endgültig die Daten ausgewählt haben, müssen Sie den Vertreter auffordern,  Placeholder seinen Kontext aufzurufen.placeholderContext und teilen Sie ihm mit, dass Sie die Informationen erhalten haben. Aktualisieren Sie anschließend Ihr Modell und den Kontext des PlatzhaltersContext. Tauschen Sie eine Zelle automatisch mit einem Platzhalter Placeholderfür eine Ihrer Zellen aus. Dies entspricht dem Typ der empfangenen Daten.

All diese Maßnahmenwir produzierendurch einen Platzhalter Kontextschaffen  placeholderContext , die einen Platzhalter steuertPlaceholder und dass Sie von Koordinator erhalten Koordinator ,fragen„Reset“DropElement Element an einen PlatzhalterPlaceholder.

Ich werde den Initialisierer für den Kontextersatz verwenden Der Platzhalterkontext , der das DragItem an den  UICollectionViewDropPlaceholder "wirft":



Das Objekt, das ich " ablegen " soll,Dropist  item.dragItem , wobei item das for -Element derSchleife ist, da wirDropviel koordinator.items werfen können. Wir "werfen" sie einzeln. Also, item.dragItem - das istwas wir „ziehen“ sindDragund „werfen“ Drop. Das nächste Argument für diese Funktion ist der Placer. Ich werde ihn mit dem Initialisierer UICollectionViewDropPlaceholder erstellen :



Um dies zu tun, muß ich wissen , wohin ich gehe , um einen Platzhalter einzufügen Placeholder, dh insertionIndexPath sowie die Kennung von recycelter Zelle  reuseIdentifier .
Argument insertionIndexPath offensichtlich ist destinationIndexPath es IndexPath  aufzunehmen „ der drag“ ein Objekt ist , das zu Beginn des Verfahrens berechnet wird performDropWith .

Sehen Sie sich nun die Wiederverwendungszellen-  ID des reuseIdentifier an . Sie müssen entscheiden , welche Art von Zelle Zelle ist Ihr ein Platzhalter Placeholder. Koordinator Koordinator Es gibt keine " vormontierte " Zelle für eine Ersatzzelle Placeholder. Sie müssen sich für diese Zelle entscheiden . Daher wird eine wiederverwendbare  Zellkennung reuseIdentifiercell mit Ihrer angefordert,storyboard damit sie als PROTOTYPE verwendet werden kann.

Ich werde es "DropPlaceholderCell" nennen, aber im Grunde könnte ich es nennen, wie Sie möchten. 
Dies ist nur eine Zeichenfolge , die ich für meine storyboardSache verwenden werde.
Gehen Sie zu uns zurück storyboardund erstellen Sie eine Zelle für den Platzhalter Placeholder. Dazu müssen wir nur eine Sammlung auswählen. Collection Viewund inspiziere es. Im allerersten Feld Itemswechsle ich 1zu 2. Dadurch wird sofort eine zweite Zelle für uns erstellt, die eine exakte Kopie der ersten ist.



Wählen Sie unsere neue Zelle aus ImageCell, setzen Sie den Bezeichner " DropPlaceholderCell", entfernen Sie alle UIElemente von dort , einschließlich Image View, da dieser PROTOTYPE verwendet wird, wenn das Bild noch nicht angekommen ist. Fügen Sie einen neuen Aktivitätsindikator aus der Objektpalette hinzu. Diese Activity Indicatorwird rotiert, um den Benutzern klar zu machen, dass einige "heruntergefallene" Daten erwartet werden. Ändern Sie auch die Hintergrundfarbe Backgroundzu verstehen , dass , wenn „Reset“ von außen Bild funktioniert genau diese Zelle Zelle als Prototypen:



Hinzufügen eines neuen Zelltyp darf nicht ImageCollectionVewCelldenn es werden keine Bilder darin sein. Ich werde diese Zelle zu einer gewöhnlichen Zelle des UIСollectionCiewCell TYPE machen , da wir keine OutletsKontrolle benötigen :



Lassen Sie uns den Aktivitätsindikator Activity Indicatorso konfigurieren , dass er von Anfang an animiert wird und ich nicht etwas in den Code schreiben muss, um ihn auszuführen. Klicken Sie dazu auf die Optionen Animating:



Und das ist alles. Also haben wir alle Einstellungen für diese Zelle vorgenommen DropPlaceholderCell, gehen Sie zurück zu unserem Code. Jetzt haben wir einen hervorragenden Ersatzlokator Placeholder, der sofort einsatzbereit ist. 

Wir müssen nur noch die Daten erhalten, und wenn die Daten empfangen werden, werden wir einfach zu diesem Kontext  Platzhalter ontontext sagen und den Platzhalter austauschenPlaceholderund unsere "native" Datenzelle, und wir werden Änderungen am Modell vornehmen.

Ich werde das EINS-Objekt "laden", in dem mein Artikel die loadObject (ofClass: UIImage.self) -Methode (Singular) verwendet. Ich verwende den Code item.dragItem.itemProvider Anbieter ItemProvider , welche Datenelemente liefert  Artikel  asynchron. Es ist klar, dass wenn iitemProvider verbunden ist , das Objekt des "Reset" -Iitem  außerhalb dieser Anwendung liegt. Die folgende Methode ist die loadObject- Methode (oflass: UIImage.self) (im Singular):



Diese spezielle Schließung wird NICHT ausgeführtmain queue. Und leider mussten wir wechseln main queuemit DispatchQueue.main.async {} , um „fangen“ das Bildseitenverhältnis auf eine lokale Variable aspectratio .

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



... und wird „fangen“ sie mit Bild - Upload - Bild und die URL URL nicht :



Wenn die beiden lokalen Variablen imageURL und  aspectratio nicht gleich null , werden wir den Kontext eines Platzhalter bitten placeholderSontext  mit der Methode commitInsertion geben uns die Möglichkeit , unser Modell zu ändern Bildergalerie



Wir haben diesen Ausdruck insertionIndexPath - es indexPath Insertion, und wir unser Modell ändern Bildergalerie . Das ist alles , was wir tun müssen, und diese Methode wird automatisch einen Platzhalter ersetzen Placeholderauf der Zell Zelle von einem normalen Methodenaufruf cellForItemAt .

Beachten Sie, dass insertIndexPath sich sehr von destinationIndexPath unterscheiden kann . Warum Da die Datenerfassung 10 Sekunden dauern kann, ist dies unwahrscheinlich, aber es kann 10 Sekunden dauern. Während dieser Zeit in der SammlungCollection Viewviel kann passieren Können neue Zellen hinzufügen Zellen geschieht alles schnell genug.

IMMER hier insertionIndexPath , und NUR insertionIndexPath , um Ihre Modelle 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 gab die entsprechenden zurück  nach Anbieter .

Dies aktualisiert unsere Model imageGallery und die commitInsertion- Methodemacht alles andere für uns. Sie müssen nichts weiter tun, keine Einfügungen, Zeilen löschen, nichts davon. Und da wir uns in einer Schließung befinden, müssen wir natürlich Selbst hinzufügen . .



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



Eine Sache, die Sie beachten solltenURLsDas kommt von solchen Orten Google, in Wirklichkeit brauchen sie eine kleine Transformation, um ein „sauberes“ URL Bild zu erhalten. Wie dieses Problem gelöst wird, ist in dieser Demo-Anwendung in einer Datei Utilities.swiftauf Github zu sehen .
Wenn Sie ein URLBild abrufen , verwenden wir daher die imageURL -Eigenschaft der URL- Klasse :



Und dies ist alles, was Sie tun müssen, um etwas außerhalb der Sammlung aufzunehmen Collection View.

Lass es uns in Aktion sehen. Wir starten gleichzeitig im Multitasking-Modus unsere Demo-Anwendung ImageGalleryund  Safari  mit einer Suchmaschine Google. Wie  Google wir suchen Bilder zum Thema „Dawn» (Sonnenaufgang). InSafariEin Drag & DropMechanismus ist bereits eingebaut , sodass wir eines dieser Bilder auswählen können, es lange halten, etwas verschieben und in die 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 seine Sammlung zu kopieren. Nachdem wir das Bild zurückgesetzt haben, dauert es einige Zeit, bis das Bild geladen ist. Zu diesem Zeitpunkt funktioniert es Placeholder:



Nachdem der Download abgeschlossen ist, wird das "verworfene" Bild an der richtigen Stelle platziert und Placeholderverschwindet:



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



Nach der "Reset" -Arbeit Placeholder:



Als Ergebnis 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  ControllerDragund Dropdelegieren und Erkennungs Geste hinzufügen Prise , die die Anzahl der Bilder pro Zeile reguliert:



Natürlich wir können den Cache für Bilder ein Imagecache :



wir füllen Imagecache , wenn „Reset“ Dropin der Methode performDrop ...



und eine Probe von „Netzwerk“ in der Benutzerklasse ImageCollectionViewCell :



A verwendete Cache Imagecache wird Zelle WiedergabeZelle unserer Bildergalerie in der Benutzerklasse ImageCollectionViewCell :



Nun beginnen wir mit einer leeren Sammlung ...



... dann "werfen" Sie ein neues Bild auf unsere Sammlung ...



... das Laden von Bildern findet statt undPlaceholder...



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



Wir füllen unsere Sammlung AUSSERHALB:



passt Bilder laden undPlaceholdersarbeiten ...



Und die Bilder erscheinen an der richtigen Stelle:



So können wir mit unserer Bildergalerie viel tun: Füllen Sie sie mit EXTERNAL, ordnen Sie die Elemente innen neu an, tauschen Sie Bilder mit anderen Anwendungen aus Niyami
Wir müssen ihr beibringen, unnötige Bilder durch "Zurücksetzen" zu beseitigen.Dropim "Mülleimer" auf der rechten Navigationsleiste. Wie im Abschnitt "Image Gallery" der Demoanwendung beschrieben , wird der "Papierkorb" von der GabageView- Klasse dargestellt , die von UIView erbt, und wir müssen lernen , Bilder aus unserer Sammlung aufzunehmen Сollection View.

Zurücksetzen DropBildergalerie in dem „Mülleimer“.


Gleich von der Fledermaus - in den Steinbruch. Ich will hinzufügen GabageView „Interaktion“ Interaktion und es wird UIDropInteraction , da ich versuche zu „reset“ ein Droppaar Dinge. Alle müssen wir dafür Sorge , dass UIDropInteraction , diese Delegierten der Delegierte , und ich werde selbst ernennen, die sich selbst , dieser Delegat der Delegierte :



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



Alles , was wir es funktioniert tun müssen Drop, Dies ist die Implementierung der uns bereits bekannten Methoden  canHandle ,sessionDidUpdate und performDrop .



Im Gegensatz zu ähnlichen Methoden für die SammlungCollection Viewhaben wir jedoch keine zusätzlichen Informationen in Form des dumpPath-Indexpfads .

Lassen Sie uns diese Methoden implementieren.
In der canHandle- Methode werden nur "Drag & Drop" verarbeitetDrag, bei denen es sich um UIImage- Bilder handelt. Daher werde ichnur dann true zurückgeben , wenn session.canLoadObjects (ofClass: UIImage.self) verwendet wird :



In der canHandle- Methode  melden Sie im Wesentlichen, dass das "Drag and Drop" -Objekt kein UIImage- Bild istdann macht es keinen Sinn, das Drop weiter zu löschen und nachfolgende Methoden aufzurufen.
Wenn das "Drag and Drop" -Objekt ein UIImage- Bild ist , führen wir die sessionDidUpdate- Methode aus . Bei dieser Methode müssen Sie lediglich unsere Rücksetzanforderung  an UIDropProposal zurückgebenDrop . Und ich bin bereit, nur das LOCALLY-Objekt "Drag & Drop" des UIImage- Bildtyps TYPE zu akzeptieren , das an beliebiger DropStelle in meiner GarbageView-Datei "entleert"  werden kann . Mein GarbageView kann nicht mit EXIT-Bildern interagieren. Daher analysiere ich mit der Variable session.localDragSessionGibt es ein lokales "drop" Drop, und ich gebe die "reset" -Klausel in Form eines UIDropProposal- Konstruktors zurück, wobei das Operationsargument auf .copy gesetzt ist , da ALWAYS LOCAL Drag & Drop Dragin meiner Anwendung aus der Auflistung stammt Collection View. Wenn AUSSEN "gezogen" Dragund "zurückgesetzt" wird Drop, gebe ich die "reset" -Klausel in Form des UIDropProposal- Konstruktors zurück, wobei das Operationsargument auf " .fobbiden" gesetzt ist , das heißt "verboten" und wir erhalten das Zeichen des "reset" -Verbots anstelle des grünen Pluszeichens . UIImage-



Image kopierenWir simulieren, dass der Maßstab auf fast 0 reduziert wird. Wenn das Zurücksetzen erfolgt, werden wir dieses Bild aus der Sammlung entfernen Collection View.
So erstellen Sie die Benutzer die Illusion von „zurückgesetzt, und das Verschwinden von“ Bild in dem „Mülleimer“, verwenden wir eine neue Methode für uns  previewForDropping , mit dem Sie die „Reset“ umleiten Dropan einem anderen Ort und zur gleichen Zeit verwandelt das „Einweg“ Objekt während der Animation:



In Bei dieser Methode erhalten wir mit dem UIDragPreviewTarget- Initialisierer ein neues preView für das Zielobjekt , das gelöscht werden soll, und leiten es mit der retargetedPreview- Methode um  .an einen neuen Ort, zum "Papierkorb", wobei die Skala auf fast Null absinkt :



Wenn der Benutzer den Finger nach oben bewegt hat , wird ein "Reset" ausgeführt  Drop, und ich (wie  GarbageView ) bekomme die Meldung performDrop . In der Nachricht  performDrop führen  wir den eigentlichen "Reset" durch  Drop. Um ehrlich zu sein,  interessiert uns das Bild selbst, das auf der GarbageView abgelegt wurde , nicht mehr, da wir es fast unsichtbar machen. Wahrscheinlich wird die Tatsache, dass das Zurücksetzen abgeschlossen ist, bedeuten,  Dropdass wir dieses Bild aus der Sammlung entfernen Collection View. Dazu müssen wir die Sammlung selbst und die Sammlung und den indexPath kennenBild in ihm abgeladen. Woher bekommen wir sie?

Da der Prozess  Drag & Dropin einer einzigen Anwendung kommt, ist es uns zur Verfügung die 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 Verwendung datasource wie  ImageGalleryCollectionViewController  und Modell Bildergalerie unsereController, können wir eine Reihe von Bildern erhalten Bilder TYPE [ImageModel]:



Mit Hilfe der lokalen DragSitzung  localDragSession unsererDropSitzung  Sitzung  konnten wir bekommen alle „drag“ auf GarbageView Drag Elemente der Elemente , und es kann eine Menge sein, wie wir wissen, und alle sind bilder unserer collectionView-sammlung . Wir habendieDragElemente der DragItems unserer Sammlung erstelltCollection View und für jedes überhöhteDragElementbereitgestellt . dragItem lokales Objekt localObject , der das Bild ist Bild , aber es istwir beiinternen Reorganisation Sammlung praktisch nicht kommen Collection , aber die „Reset“ Bildergalerie „Abfalleimer“ wir in den lokalen Einrichtung dringend brauchen localObject „drag“ Objekt dragItem , nach all dieser Zeit Wir haben keinen Koordinator, der Informationen über das, was in der collectionView- Sammlung passiert, so großzügig austauscht. Daher möchten wir, dass das localObject- Objektder indexPath  in den Image-Array- Images unseres Modells istBildergalerie . 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 sendenes an den ArrayIndizes Indizes  und Array indexPahes Bilder löschen:



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



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



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



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



Sie reduziert fast zu 0 ...



... und verschwinden aus der Sammlung Collection View, versteckt in der "Mülltonne":



Bilder zwischen Läufen speichern.



Um die Bildergalerie zwischen den Starts zu speichern, verwenden wir UserDefaults . Zuerst konvertieren Sie unser Modell in ein JSONFormat. Um dies zu tun, werden wir in unseren hinzufügen ControllerVariable var defailts ...



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



String String Arrays von der Array , die URL und Doppel bereits das Protokoll implementiert Kodierbare , so haben wir nicht , etwas anderes zu tun , um Codierung zu arbeiten und Decodierung für das ImageGallery- Modell im JSONFormat.
Wie erhalten wir eine JSONVersion von ImageGallery ?
So erstellen Sie diese erweiterte Variable var json , die das Ergebnis der Versuche zurück zu sich selbst zu umwandeln, selbst über JSONEncoder.encode () im JSONFormat:



Und das ist alles. Entweder werden Daten als Ergebnis der Selbstkonvertierung in das Format zurückgegeben JSON, oder Null, wenn diese Konvertierung nicht durchgeführt werden kann. Letzteres geschieht jedoch nicht, da dieser TYPE zu 100% kodierbar ist . Die optionale Json- Variable wird einfach aus Symmetriegründen verwendet.
Jetzt haben wir eine Art und Weise Modelle zu konvertieren Bilddatenbank in Daten - Format JSON. In diesem Fall wird die Variable json ist Typ Daten? die in userdefaults gespeichert werden kann .
Nun stellen wir uns vor, dass wir es irgendwie geschafft haben, Json-JSON Daten zu erhalten , und ich möchte unser Model, eine Instanz der ImageGallery- Struktur, von ihnen neu erstellen . Dazu ist es sehr einfach, einen INITIALIZER für ImageGallery zu schreiben , dessen Eingabeargument json data ist . Dieser Initialisierer ist ein "fallender" Initialisierer (JSONfailable). Wenn es nicht initialisiert wird, es „fällt“ und kehrt nil :



Ich bin nur einen neuen Wert bekommen newValue durch den Decoder JSONDecoder und versucht , die Daten zu dekodieren die json , die zu meiner initializer übertragen werden, und dann ordnet sie dem Selbst .
Wenn ich das geschafft habe, bekomme ich eine neue Instanz von ImageGallery , aber wenn mein Versuch fehlschlägt, gebe ich null zurück , weil 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 Daten vorliegenkann beschädigt oder leer sein, was alles zum "Fall" ( fail) des Initialisierers führen kann.

Nun können wir das READ implementieren JSONDaten und Wiederherstellungsmodell Bildergalerie der Methode ViewWillAppear unsere Controller...



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



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



Wenn wir die Anwendung zu schließen und wieder öffnen, können wir unsere bisherige Galerie sehen Bilder, die in UserDefaults gespeichert werden .

Fazit


In diesem Artikel wird am Beispiel einer sehr einfachen Demo-Anwendung "Image Gallery" gezeigt, wie einfach es ist, Technologie Drag & Dropin eine iOSAnwendung einzubetten . Dies ermöglichte es uns, die Bildergalerie vollständig zu bearbeiten, neue Bilder dort aus anderen Anwendungen zu „werfen“, vorhandene zu verschieben und unnötige zu entfernen. Außerdem können Sie die in der Galerie gesammelten Bilder an andere Anwendungen weitergeben.

Natürlich möchten wir viele solcher thematischen Bildersammlungen erstellen und direkt auf dem iPad oder auf iCloud Drive speichern. Dies ist möglich, wenn jede dieser Galerien als dauerhaft gespeichertes UID- Dokument interpretiert wird .. Eine solche Interpretation wird es uns ermöglichen, die nächste Abstraktionsebene zu erreichen 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. Sie können damit UIDocument- Dokumente des Typs "Gallery of Images" sowohl auf als auch auf Ihrem Computer erstellen iPadund iCloud Drivedas erforderliche Dokument zum Anzeigen und Bearbeiten auswählen.
Dies ist jedoch das Thema des nächsten Artikels.

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


 

Jetzt auch beliebt: