
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:
- Zunächst werden wir unserer
Collection View
Möglichkeit geben, Drag- from-UIImage- Bilder sowohl extern als auch lokal zu ziehen. - Dann werden wir unserer Sammlung von
Collection View
beibringen, "gezogenes" Drag
extern oder lokal von UIImage zu akzeptieren. - 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 Drag
lokal "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“ Drag
außerhalb unserer Sammlung Collection View
in andere Anwendungen. Wenn wir jedoch Drag
lokal „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“ Drag
abgebrochen.
Neben lokalen Objekt localObject , können Sie den lokalen Kontext erinnern localContext für unsere Drag
Sitzung die Sitzung . In unserem Fall wird es eine Sammlung von seiner Kollektion und es ist nützlich , um uns danach:
Mit „Drag and Drop“ Drag
können Sie weitere Elemente hinzufügen , Elemente diesen „drag and drop“, nur die Geste tun tippt auf sie. Als Ergebnis können Sie ziehenDrag
viele 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 Photos
Mechanismus bereits in die Anwendung integriert istDrag & Drop
dann funktioniert alles gut und es ist cool.Das "Ziehen" Drag
und "Ablegen" des Drop
Galeriebilds 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.Drop
Bilder auf Sammlung zurücksetzenCollection View
Jetzt müssen wir einen Drop
Teil für meine Sammlung erstellen Collection View
, damit wir Drop
alle "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 Fix
und 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 Apple
Methoden gibt, die Sie für das Drop
Teil 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 View
sieht 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 Drop
Bild Collection View
AUSSERHALB in meine Sammlung zu "kopieren" .Wenn das „Reset“ Drop
das 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 Drop
Bildes jedoch AUSSEN erfolgt, sollten wir nur das "Ziehen und Ablegen" behandeln Drag
, das das UIImage- Bild zusammen mit URL
diesem 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 Drop
der Sitzung die Sitzung sowohl die lokale Drag
Sitzung localDragSession . Diese lokale Drag
Sitzung 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" Drop
AUSSEN.Die canHandle- Methode gibt an , dass wir diese Art von Drag & Drop nur für Drag
unsere 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 , iOS
unter Verwendung des Verfahrens dropSessionDidUpdate Delegierten UICollectionViewDropDelegate über unser Angebot UIDropProposal zurückgesetzt zu implementieren Drop
.Bei dieser Methode müssen wir einen Drop
Satz 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 . ParameterCollection View
subclass
Collection View
Mit Absicht wird der Sammlung mitgeteilt ,Collection View
ob 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 View
verkü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 View
mit dieser kleinen Informationtun kann, die wir ihr zur Verfügung gestellt haben.Ich ziehe das Bild ausSafari
der 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 Safari
und klicken Es werden bereits 3 "gezogene" Bilder angezeigt:
Wenn ich jedoch meinen Finger hebe und Drop
diese 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 View
bereits weiß, was ich tun möchte.Die Sammlung Collection View
ist eine absolut wunderbare Sache für den Mechanismus.Drag & Drop
Sie 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
. Kehrenwir 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 Drop
aus meiner collectionView- Sammlung ein "Zurücksetzen" erfolgt , muss ich das Drop
Sammlungselement 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“ Drop
durchgeführt wird aus einer anderen Anwendung heraus, dann müssen wir die Eigenschaft verwenden ItemProvider «gezogen» Element Element , um Daten abzurufen.Wenn wir Drop
in 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“ Drag
von 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 & Drop
nicht 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 Drop
Bilder 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 Drag
und ablegen " und " ablegen" ”Drop
Werden 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 Apple
schlug für ollection View
diesen Fall eine völlig neue Technologie für die Verwendung von Ersatzstoffen vor Placeholders
.Sie platzieren einen Collection View
Platzhalter in Ihrer Sammlung Placeholder
, und die Sammlung Collection View
verwaltet alles für Sie. Wenn die Daten endgültig ausgewählt sind, müssen Sie den Platzhalter lediglich auffordern, Placeholder
seinen 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 Placeholder
gegen 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 Placeholder
und dass Sie von Koordinator erhalten Koordinator , „Reset“ zu fragen Drop
Element 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, Drop
ist item.dragItem , wobei item ein for- Element einer Schleife ist, da wir Drop
viele koordinator.items werfen können . Wir "werfen" sie eins nach dem anderen. Also, item.dragItem - das ist , was wir „ziehen“ sind Drag
und „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 werdePlaceholder
d.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 storyboard
diese Sache zu schaffen.Gehen Sie zurück zu unserer storyboard
und erstellen Sie eine Zellzelle für den Platzhalter Placeholder
. Dazu müssen wir nur eine Sammlung auswählen Collection View
und untersuchen. Im allerersten Feld Items
wechsle ich 1
zu2
. 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 UI
Elemente 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 Background
zu 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 Outlets
für die Steuerung benötigen :
Konfigurieren Sie den Aktivitätsindikator Activity Indicator
so, 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 DropPlaceholderCell
und kehren zu unserem Code zurück. Jetzt haben wir einen großartigen Locator Placeholder
bereit.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 ausPlaceholder
und 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 queue
mit 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 Placeholder
durch 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 View
kann 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 URL
Bild 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, URLs
dass sie von Orten wie diesem stammen Google
. In Wirklichkeit benötigen sie kleinere Transformationen, um „sauber“ zu werden.URL
für das Bild. Wie dieses Problem gelöst wird, können Sie dieser Demoanwendung in einer Datei Utilities.swift
auf Github entnehmen .Daher verwenden URL
wir 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 ImageGallery
und Safari
mit einer Suchmaschine Google
. In Google
suchen wir nach Bildern zum Thema "Dawn" (Sonnenaufgang). In Safari
bereits gebautDrag & Drop
Der 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 Placeholder
verschwindet:
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
Drag
und Drop
delegieren 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" Drop
in 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 undPlaceholder
funktioniert ...
... und das Bild erscheint an der richtigen Stelle:
Wir füllen unsere Sammlung weiterhin AUSSEN:
Kommt Das Laden von BildernPlaceholders
funktioniert ...
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“ beseitigtDrop
im "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 Drop
Bilder 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 Drop
paar 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 View
verfü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" Drop
gibt 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" Drag
in meiner Anwendung aus der Sammlung stammt Collection View
. Wenn "Drag Drag
& Drop" und "Zurücksetzen" Drop
AUSSEN 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" Drop
an 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 Drop
signalisiert 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 & Drop
in einer einzigen Anwendung kommt, ist es uns zur Verfügung alle lokalen: lokale Drag
Sitzung localDragSession unsere Drop
Sitzung 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 lokalenDrag
Sitzung localDragSession unsererDrop
Sitzung 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 . ErstellenDrag
Elementen dragItems unserer SammlungCollection View
haben wir für jeden „drag“ vorgesehenDrag
ElementdragItem 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 JSON
Format konvertiert haben. Um dies zu tun, wird fügen wir unseren Controller
Variable 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 JSON
Format.Wie bekommen wir die JSON
Version 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 JSON
Format 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 (JSON
failable
) 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 JSON
Daten 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 & Drop
in eine iOS
Anwendung 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 iPad
und iCloud Drive
das 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 & Drop
und nach der Implementierung des Mechanismus befindet sich auf Github .