
Mekanisme
Drag & Drop
yang berjalan di
iOS 11
dan
iOS 12
adalah cara untuk menyalin atau memindahkan data secara asinkronik dalam satu aplikasi dan antar aplikasi yang berbeda. Meskipun teknologi ini berusia sekitar 30 tahun, teknologi ini benar-benar menjadi "terobosan" di
iOS
karena ketika menyeret sesuatu di
iOS
,
multitouch
memungkinkan Anda berinteraksi secara bebas dengan seluruh sistem dan mengumpulkan data untuk diatur ulang dari aplikasi yang berbeda.
iOS
memungkinkan untuk menangkap beberapa elemen sekaligus. Selain itu, mereka tidak harus berada dalam aksesibilitas yang mudah untuk seleksi: Anda dapat mengambil objek pertama, lalu pergi ke aplikasi lain dan mengambil sesuatu yang lain - semua objek akan dikumpulkan dalam "tumpukan" di bawah jari Anda. Kemudian panggil dok universal di layar, buka aplikasi apa saja di sana dan tangkap objek ketiga, lalu buka layar dengan aplikasi berjalan dan, tanpa melepaskan objek, buang mereka ke salah satu program yang terbuka. Kebebasan bertindak seperti itu dimungkinkan di
iPad
, di
iPhone
, cakupan
Drag & Drop
di
iOS
terbatas pada kerangka kerja satu aplikasi.
Aplikasi paling populer (
Safary
,
Chrome
,
IbisPaint X
,
Mail
,
Photos
,
Files
, dll.) Sudah memiliki mekanisme
Drag & Drop
. Selain itu,
Apple
memberi para pengembang
API
sangat sederhana dan intuitif untuk menyematkan mekanisme
Drag & Drop
di aplikasi Anda. Mekanisme
Drag & Drop
, sama seperti gerakan, bekerja di
UIView dan menggunakan konsep
Interaksi , sedikit seperti gerakan, sehingga Anda dapat memikirkan mekanisme
Drag & Drop
seperti gerakan yang sangat kuat.
Itu, serta gerakan, sangat mudah untuk diintegrasikan ke dalam aplikasi Anda. Terutama jika aplikasi Anda menggunakan tabel
UITableView atau koleksi
UICollectionView , karena bagi mereka
API Drag & Drop
ditingkatkan dan dinaikkan ke tingkat abstraksi yang lebih tinggi dalam arti bahwa
Collection View
itu sendiri membantu Anda dengan
indexPath dari elemen koleksi yang ingin Anda seret dan jatuhkan
Drag
. Dia tahu di mana jari Anda berada dan mengartikannya sebagai
indexPath dari elemen koleksi yang Anda "seret"
Drag
saat ini, atau sebagai
indexPath dari elemen koleksi di mana Anda "menjatuhkan"
Drop
sesuatu. Jadi
Collection View
menyediakan Anda dengan
indexPath , tetapi sebaliknya itu benar-benar
API Drag & Drop
sama seperti
UIView biasa.
Proses
Drag & Drop
di
iOS
memiliki 4 fase berbeda:
Angkat
Angkat (lifting) - ini adalah saat pengguna melakukan gerakan
tekan lama , yang menunjukkan elemen yang akan "seret dan lepas". Pada saat ini, yang disebut "
lift preview
" yang sangat ringan dari elemen yang ditunjukkan terbentuk, dan kemudian pengguna mulai menyeret jari-jarinya.

Seret (seret dan lepas)
Seret (seret dan lepas) - ini adalah saat pengguna memindahkan objek di permukaan layar. Selama fase ini, "
lift preview
" untuk objek ini dapat dimodifikasi (tanda tambah "+" berwarna hijau atau tanda lainnya muncul) ...

... beberapa interaksi dengan sistem juga diperbolehkan: Anda dapat mengklik pada beberapa objek lain dan menambahkannya ke sesi "seret dan lepas" saat ini:

Jatuhkan
Drop terjadi ketika pengguna mengangkat jari. Pada saat ini, dua hal dapat terjadi: objek
Drag
akan dihancurkan, atau objek
Drop
akan "jatuh" di tujuan.

Transfer Data
Jika proses Seret "
seret dan lepas" tidak dibatalkan dan setel "
Drop " terjadi, maka
Transfer Data (transfer data) terjadi, saat "titik drop" meminta data dari "sumber", dan transfer data asinkron terjadi.
Dalam tutorial ini, kami akan menunjukkan kepada Anda cara
mengintegrasikan mekanisme
Drag & Drop
ke dalam aplikasi
iOS
Anda dengan mudah menggunakan aplikasi demo Galeri Gambar, yang diambil
dari kursus pekerjaan rumah Stanford CS193P .
Kami akan menganugerahkan
Collection View
kemampuan untuk mengisi diri kita sendiri dengan gambar-gambar DI LUAR, serta mengatur kembali elemen-elemen DALAM dengan menggunakan mekanisme
Drag & Drop
. Selain itu, mekanisme ini akan digunakan untuk membuang elemen yang tidak perlu dari
Collection View
ke dalam "tong sampah", yang merupakan
UIView normal dan diwakili oleh tombol pada panel navigasi. Kami juga dapat berbagi gambar yang dikumpulkan di Galeri kami dengan aplikasi lain menggunakan mekanisme
Drag & Drop
, misalnya,
Notes
atau
Notes
atau
Mail
atau perpustakaan foto (
Photo
).
Tetapi sebelum fokus pada implementasi mekanisme
Drag & Drop
dalam aplikasi demo "Galeri Gambar", saya akan membahas komponen utamanya secara singkat.
Fitur dari aplikasi demo "Galeri Gambar"
Antarmuka pengguna (
UI
) aplikasi Galeri Gambar sangat sederhana. Ini adalah "cuplikan layar" dari
Image Gallery Collection View Controller
dimasukkan ke
Navigation Controller
:

Bagian utama dari aplikasi tentu saja adalah
Image Gallery Collection View Controller
, yang didukung oleh kelas
ImageGalleryCollectionViewController dengan Model Galeri Gambar sebagai variabel
var imageGallery = ImageGallery () :

Model diwakili oleh struktur
ImageGallery struct yang berisi larik gambar
gambar , di mana setiap gambar dijelaskan oleh struktur
ImageModel struct yang berisi
URL
url lokasi gambar (kami tidak akan menyimpan gambar itu sendiri) dan rasio aspeknya:
ImageGalleryCollectionViewController kami mengimplementasikan protokol
DataSource :

Sel khusus dalam koleksi sel berisi gambar
imageView: UIImageView! dan indikator aktivitas
pemintal: UIActivityIndicatorView! dan didukung oleh
subclass
kustom
ImageCollectionViewCell dari kelas
UICollectionViewCell :

Public API
dari kelas
ImageCollectionViewCell adalah
URL gambar
imageURL . Segera setelah kami menginstalnya,
UI
kami diperbarui, yaitu, data untuk gambar dipilih secara tidak sinkron pada
gambar iniURL dan ditampilkan dalam sel. Ketika data sedang diambil dari jaringan, indikator aktivitas
pemintal bekerja, menunjukkan bahwa kita sedang dalam proses mengambil data.
Saya menggunakan antrian global
(qos: .userInitiated) dengan argumen kualitas layanan
qos untuk mendapatkan data pada
URL
diberikan, yang diatur ke
.userInitiated , karena saya memilih data berdasarkan permintaan pengguna:

Setiap kali Anda menggunakan variabel Anda sendiri di dalam penutupan, dalam kasus kami itu adalah
imageView dan
imageURL , kompiler memaksa Anda untuk menempatkan
diri di depan mereka
. sehingga Anda bertanya pada diri sendiri: "Apakah ada" tautan siklik memori "?" Kami tidak memiliki "
memory cycle
" eksplisit di sini, karena
diri sendiri tidak memiliki pointer ke penutupan ini.
Namun, dalam kasus multithreading, Anda harus ingat bahwa
sel -
sel dalam
Collection View
dapat digunakan kembali berkat metode
dequeueReusableCell . Setiap kali sel (baru atau digunakan kembali) muncul di layar, gambar diunduh dari jaringan secara tidak sinkron (saat ini "
pemintal " indikator aktivitas
pemintalan sedang berputar).
Segera setelah unduhan selesai dan gambar diterima,
UI
sel koleksi ini diperbarui. Tapi kami tidak menunggu gambar dimuat, kami terus menggulung koleksi dan sel koleksi yang kami tandai meninggalkan layar tanpa memperbarui
UI
kami. Namun, gambar baru akan muncul di bawah dan sel yang sama yang telah meninggalkan layar akan digunakan kembali, tetapi untuk gambar lain, yang dapat dengan cepat memuat dan memperbarui
UI
. Pada saat ini, pemuatan gambar yang dimulai sebelumnya dalam sel ini akan kembali dan layar akan diperbarui, yang akan mengakibatkan hasil yang salah. Ini karena kami menjalankan berbagai hal yang bekerja dengan jaringan di utas yang berbeda. Mereka kembali pada waktu yang berbeda.
Bagaimana kita dapat memperbaiki situasi?
Dalam kerangka mekanisme
GCD yang kami gunakan, kami tidak dapat membatalkan unduhan gambar sel yang meninggalkan layar, tetapi kami dapat, ketika data
imageData kami
berasal dari jaringan, periksa
url URL
yang menyebabkan pemuatan data ini dan bandingkan dengan yang diinginkan pengguna. sel ini saat ini, mis.
imageURL . Jika tidak cocok, maka kami tidak akan memperbarui sel
UI
dan menunggu data gambar yang kami butuhkan:

Baris kode
url yang tampaknya tidak masuk akal ini
== self.imageURL membuat semuanya berfungsi dengan benar di lingkungan multi-utas yang memerlukan imajinasi non-standar. Faktanya adalah bahwa beberapa hal dalam pemrograman multi-threaded terjadi dalam urutan yang berbeda dari kode yang ditulis.
Jika tidak mungkin untuk memilih data gambar, gambar dihasilkan dengan pesan kesalahan dalam bentuk string "Kesalahan" dan emoji dengan "kerutan". Hanya ruang kosong di
Collection View
kami yang dapat sedikit membingungkan pengguna:

Kami tidak ingin gambar dengan pesan kesalahan mengulangi
aspekRasio gambar kesalahan ini, karena dalam hal ini teks bersama dengan emoji akan ditarik atau dikompresi. Kami ingin itu menjadi netral - kotak, yaitu, ia akan memiliki aspek rasio aspek mendekati 1,0.

Kami harus memberi tahu
Controller
kami tentang keinginan ini sehingga ia mengoreksi aspek rasio aspek untuk
indexPath yang sesuai di Model
imageGallery- nya. Ini adalah tugas yang menarik, ada banyak cara untuk menyelesaikannya, dan kami akan memilih yang termudah dari mereka - menggunakan
Opsional penutupan
var closAspectRatio: (() -> Void)? . Dapat menyamai
nol dan tidak perlu diinstal jika ini tidak perlu:

Saat memanggil closure
changeAspectRatio? () Dalam kasus pengambilan data yang salah, saya menggunakan rantai
opsional . Sekarang siapa pun yang tertarik pada beberapa jenis pengaturan saat menerima gambar yang salah dapat mengatur penutupan ini untuk sesuatu yang spesifik. Dan inilah yang kami lakukan di
Controller
kami dalam metode
cellForItemAt :

Detail dapat ditemukan di
sini .
Untuk menampilkan gambar dengan
aspekRasio yang benar, metode
sizeForItemAt dari delegasi
UICollectionViewDelegateFlowLayout digunakan :

Selain
Collection View
gambar
Collection View
, di
UI
kami
UI
kami menempatkan
Bar Button
di panel navigasi dengan gambar
GarbageView khusus yang berisi "tong sampah" sebagai
subview :

Pada gambar ini, warna latar belakang untuk
GarbageView sendiri dan tombol
UIButton dengan gambar "tempat sampah" (sebenarnya ada latar belakang transparan) secara khusus diubah sehingga Anda melihat bahwa pengguna yang "membuang" gambar Galeri ke "tempat sampah" lebih banyak ruang untuk bermanuver ketika "menjatuhkan"
Drop daripada hanya ikon tempat sampah.
Kelas
GarbageView memiliki dua inisialisasi dan keduanya menggunakan metode
setup () :

Dalam metode
setup () , saya juga menambahkan
myButton sebagai
subview dengan gambar "tong sampah" yang diambil dari
Bar Button
standar
Bar Button
:

Saya menetapkan latar belakang transparan untuk
GarbageView :

Ukuran tong sampah dan posisinya akan ditentukan dalam metode
layoutSubviews () dari kelas
UIView , tergantung pada
batasan UIView yang diberikan:

Ini adalah versi awal dari aplikasi demo "Galeri Gambar", ini terletak di
Github
di folder
ImageGallery_beginning
. Jika Anda menjalankan versi aplikasi "Galeri Gambar" ini, Anda akan melihat hasil aplikasi yang bekerja pada data uji, yang kemudian akan kami hapus dan akan mengisi "Galeri Gambar" secara eksklusif di luar:

Rencana untuk menerapkan mekanisme
Drag & Drop
di aplikasi kami adalah sebagai berikut:
- pertama, kita akan memberkahi
Collection View
kita Collection View
kemampuan untuk "menyeret" Drag
DARI itu gambar UIImage baik secara eksternal maupun lokal, - maka kami akan mengajarkan koleksi gambar
Collection View
untuk menerima "seret" Drag
eksternal atau lokal dari UIImage , - kami juga akan mengajarkan GarbageView kami dengan tombol tempat sampah untuk menerima gambar UIImage yang diseret dari
Collection View
lokal dan menghapusnya dari Collection View
Jika Anda pergi ke akhir tutorial ini dan menyelesaikan semua perubahan kode yang diperlukan, Anda akan menerima versi final dari aplikasi demo "Galeri Gambar", di mana mekanisme
Drag & Drop
telah dilaksanakan. Itu terletak di
Github
di folder
ImageGallery_finished
.
Kinerja mekanisme
Drag & Drop
di
Collection View
Anda disediakan oleh dua delegasi baru.
Metode
delegasi pertama, dragDelegate , dikonfigurasikan untuk menginisialisasi dan mengkustomisasi Drag dan Drop
Drags
.
Metode delegasi kedua,
dropDelegate , selesaikan drag dan drop
Drags
dan, pada dasarnya, memberikan pengaturan
Data transfer
data dan animasi khusus ketika
Drop
diatur ulang, serta hal-hal serupa lainnya.
Penting untuk dicatat bahwa kedua protokol ini sepenuhnya independen. Anda dapat menggunakan satu atau protokol lain jika Anda hanya perlu "menyeret"
Drag
atau hanya "menjatuhkan"
Drop
, tetapi Anda dapat menggunakan kedua protokol sekaligus dan
Drag
dan jatuhkan
Drag
dan "jatuhkan"
Drop
, yang membuka fungsionalitas tambahan
Drag & Drop
mekanisme untuk menyusun ulang item di
Collection View
Anda.
Seret dan Jatuhkan Elemen Drag
DARI Collection View
Menerapkan protokol
Drag
sangat sederhana, dan hal pertama yang harus selalu Anda lakukan adalah mengatur diri
sendiri sebagai delegasi
dragDelegate :

Dan, tentu saja, di bagian paling atas kelas
ImageGalleryCollectionViewController, Anda harus mengatakan "Ya," kami menerapkan protokol
UICollectionViewDragDelegate :

Segera setelah kami melakukan ini, kompiler mulai “mengeluh”, kami klik pada lingkaran merah dan kami ditanya: “Apakah Anda ingin menambahkan metode yang diperlukan dari protokol
UICollectionViewDragDelegate ?”
Saya menjawab: "Tentu saja saya mau!" dan klik tombol
Fix
:

Satu-satunya metode yang diperlukan dari protokol
UICollectionViewDragDelegate adalah metode
itemsForBeginning , yang akan memberi tahu sistem
Drag
kita “drag and drop”. Metode
itemsForBeginning dipanggil ketika pengguna mulai "menyeret" (
Dragging
) sel di dalam
sel koleksi.
Perhatikan bahwa dalam metode ini,
Collection View
telah menambahkan
indexPath . Ini akan memberi tahu kita elemen koleksi mana,
indexPath mana, yang akan kita “drag and drop”. Ini benar-benar sangat nyaman bagi kami, karena itu adalah aplikasi yang bertanggung jawab untuk menggunakan argumen
sesi dan
indexPath untuk
mencari tahu bagaimana menangani drag dan drop
Drag
.
Jika array
[UIDragItems] dari elemen "
draggable " dikembalikan, "drag" dari
Drag
diinisialisasi, jika array kosong
[] dikembalikan, "drag" dari
Drag
diabaikan.
Saya akan membuat fungsi
dragItems (at: indexPath) pribadi kecil dengan argumen
indexPath . Ia mengembalikan array
[UIDragItem] yang kita
butuhkan .

Seperti apa
tampilan UIDragItem drag-and-drop?
Dia hanya memiliki satu hal yang sangat PENTING yang disebut
itemProvider .
itemProvider hanyalah sesuatu yang dapat menyediakan data yang akan diseret.
Dan Anda berhak bertanya: "Bagaimana dengan" menyeret dan menjatuhkan "elemen
UIDragItem yang tidak memiliki data?" Item yang ingin Anda seret mungkin tidak memiliki data, misalnya, karena membuat data ini adalah operasi yang mahal. Ini mungkin gambar
gambar atau sesuatu yang memerlukan pengunduhan data dari Internet. Hebatnya adalah bahwa operasi
Drag & Drop
benar-benar tidak sinkron. Saat Anda mulai menyeret dan menjatuhkan
Drag
, itu benar-benar objek yang sangat ringan (
lift preview
), Anda seret ke mana-mana dan tidak ada yang terjadi selama "seret" ini. Tapi begitu Anda "menjatuhkan"
Drop
suatu tempat objek Anda, kemudian, menjadi
itemProvider , itu benar-benar harus menyediakan objek "diseret" dan "dilempar" dengan data nyata, bahkan jika itu membutuhkan waktu tertentu.
Untungnya, ada banyak
itemProvider bawaan . Ini adalah kelas yang sudah ada di
iOS
dan yang merupakan
itemPoviders , seperti, misalnya,
NSString
, yang memungkinkan Anda untuk menarik dan melepas teks tanpa font. Tentu saja, ini adalah gambar
UIImage . Anda dapat memilih dan seret dan letakkan
seluruh gambar
UIImages . Kelas
NSURL , yang benar-benar luar biasa. Anda dapat pergi ke halaman
Web
, pilih
URL
dan “jatuhkan” di mana pun Anda inginkan. Ini bisa berupa tautan ke artikel atau
URL
untuk gambar, karena akan ada di demo kami. Ini adalah kelas warna dari
UIColor ,
elemen peta
MKMapItem , kontak
CNC dari buku alamat, Anda dapat memilih dan menyeret banyak hal. Semuanya adalah
itemProviders .
Kita akan "drag and drop" gambar
UIImage . Itu terletak di sel sel
Collection View
dengan
indexPath , yang membantu saya memilih sel
sel , dapatkan
Outlet
imageView dari sana dan dapatkan gambar
gambarnya .
Mari ungkapkan ide ini dengan beberapa baris kode.
Pertama, saya meminta
Collection View
saya tentang
sel untuk elemen
item yang terkait dengan
indexPath ini.

Metode
cellForItem (at: IndexPath) untuk
Collection View
hanya berfungsi untuk sel yang terlihat, tetapi, tentu saja, itu akan berfungsi dalam kasus kami, karena saya “drag and drop” elemen koleksi
Drag
di layar dan itu terlihat.
Jadi, saya mendapat sel "yang bisa diseret".
Selanjutnya, saya menggunakan operator
as? ke sel ini sehingga memiliki JENIS
subclass
kustom saya. Dan jika ini berhasil, maka saya mendapatkan
Outlet
imageView , dari mana saya mengambil gambar
gambarnya . Saya baru saja "menangkap" gambar
gambar untuk
indexPath ini.
Sekarang saya memiliki gambar
gambar , yang harus saya lakukan adalah membuat salah satu
UIDragItems ini menggunakan gambar
gambar yang dihasilkan sebagai
itemProvider , yaitu hal yang menyediakan data kepada kami.
Saya bisa membuat
dragItem menggunakan konstruktor
UIDragItem , yang mengambil
itemProvider sebagai argumen:

Kemudian kami membuat
itemProvider untuk gambar
gambar juga menggunakan konstruktor
NSItemProvider . Ada beberapa konstruktor untuk
NSItemProvider , tetapi di antara mereka ada satu yang sangat bagus -
NSItemProvider (objek: NSItemProviderWriting) :

Anda cukup memberikan objek
objek ke konstruktor
NSItemProvider ini, dan ia tahu cara membuat
itemProvider darinya. Sebagai objek seperti itu
, saya memberikan gambar gambar
gambar yang saya terima dari sel
sel dan mendapatkan
itemProvider untuk
UIImage .
Dan itu saja. Kami membuat
dragItem dan mengembalikannya sebagai array yang memiliki satu elemen.
Tapi sebelum aku kembali dragItem , aku akan melakukan satu hal lagi, yaitu, untuk mengatur variabel localObject untuk dragItem , sama dengan gambar yang dihasilkan gambar .
Apa artinya ini?
Jika Anda melakukan "seret dan lepas" Drag
secara lokal, yaitu di dalam aplikasi Anda, maka Anda tidak perlu melewati semua kode ini yang terkait dengan itemProvider , melalui pengambilan data yang tidak sinkron. Anda tidak perlu melakukan ini, Anda hanya perlu mengambil localObject dan menggunakannya. Ini adalah semacam "korsleting" dengan "drag and drop" lokal Drag
.Kode yang kami tulis akan berfungsi ketika "menyeret" di Drag
luar koleksi kami Collection View
ke aplikasi lain, tetapi jika kami "menyeret" Drag
secara lokal, kami dapat menggunakan localObject . Selanjutnya, saya mengembalikan array dari satu elemen dragItem .Omong-omong, jika karena alasan tertentu saya tidak bisa mendapatkan gambar untuk sel sel ini , maka saya mengembalikan array kosong [] , ini berarti bahwa "seret dan lepas" Drag
dibatalkan.
Selain objek lokal localObject , Anda dapat mengingat konteks lokal localContext untuk kami Drag
sesi sesi . Dalam kasus kami ini akan menjadi koleksi CollectionView dan hal ini berguna untuk kami setelah itu:
Memiliki "drag and drop" Drag
, Anda dapat menambahkan item item ini "drag and drop", hanya melakukan gerakan tap pada mereka. Hasilnya, Anda dapat menarikDrag
banyak item sekaligus. Dan ini mudah diimplementasikan dengan metode delegasi lain , UICollectionViewDragDelegate , sangat mirip dengan metode itemsForeginning , metode bernama itemsForAddingTo . Metode itemsForAddingTo terlihat persis sama dengan metode itemsForeginning dan mengembalikan hal yang sama persis, karena itu juga memberi kita indexPath tentang apa yang "direkam" oleh pengguna selama proses "seret Drag
dan lepas" , dan saya hanya perlu mendapatkan gambar gambar dari sel ke yang "direkam" oleh pengguna, dan mengembalikannya.
Kembalikan array kosong [] dari metode itemsForAddingTomengarah pada fakta bahwa gerakan tap akan ditafsirkan dengan cara biasa, yaitu, sebagai pilihan sel sel ini .Dan hanya itu yang kita butuhkan untuk drag and drop Drag
.Kami meluncurkan aplikasi.Saya memilih gambar "Venice", tahan sebentar dan mulai bergerak ...
... dan kita benar-benar dapat menyeret gambar ini ke dalam aplikasi Photos
, seperti yang Anda lihat tanda tambah hijau "+" di sudut kiri atas gambar "draggable". Saya dapat melakukan gerakan tap pada gambar Artika lain dari koleksi Collection View
...
... dan sekarang kita dapat memasukkan dua gambar ke dalam aplikasi Photos
:
Karena Photos
mekanisme sudah ada di dalam aplikasiDrag & Drop
maka semuanya bekerja dengan baik dan itu keren.Jadi, "menyeret" Drag
dan "membuang" Drop
gambar Galeri ke aplikasi lain berfungsi untuk saya , saya tidak perlu berbuat banyak dalam aplikasi saya, kecuali untuk memberikan gambar gambar sebagai array [UIDragItem] . Ini adalah salah satu dari banyak fitur hebat mekanisme Drag & Drop
- sangat mudah untuk membuatnya bekerja di kedua arah.Setel Ulang Drop
Gambar Ke KoleksiCollection View
Sekarang kita perlu membuat Drop
bagian untuk koleksi saya Collection View
sehingga kita dapat "membuang Drop
" gambar "yang diseret" di dalam koleksi ini. Gambar "dapat diseret" dapat "datang" dari LUAR, atau langsung DI DALAM koleksi ini.Untuk melakukan ini, kita melakukan hal yang sama dilakukan untuk mendelegasikan dragDelegate , yaitu membuat diri kita, diri , mendelegasikan dropDelegate dalam metode viewDidLoad :
Sekali lagi, kita harus mendaki ke puncak kelas kami ImageGalleryCollectionViewController dan memverifikasi protokol implementasi UICollectionViewDropDelegate :
Segera setelah kami menambahkan protokol baru kami, kompiler lagi mulai "mengeluh" bahwa kami tidak mengimplementasikan protokol ini. Kami mengklik tombol Fix
dan metode yang diperlukan dari protokol ini muncul di depan kami. Dalam hal ini, kami diberitahu bahwa kami harus menerapkan metode performDrop :
Kami harus melakukan ini, jika tidak “reset” tidak akan terjadi Drop
. Sebenarnya, saya akan mengimplementasikan metode performDrop terakhir, karena ada beberapa Apple
metode lain yang sangat direkomendasikan yang perlu Anda terapkan untuk Drop
bagian itu. Ini adalah canHandle dan dropSessionDidUpdate :
Jika kita menerapkan dua metode ini, maka kita bisa mendapatkan tanda tambah hijau kecil "+" ketika kita menyeret gambar dari LUAR ke koleksi kami ollection View
, dan di samping itu, mereka tidak akan mencoba untuk membuang apa yang tidak kita mengerti.Mari kita menerapkan canHandle . Kita memiliki versi Anda dari metode canHandle , yang dimaksudkan untuk pengumpulan ollection View
, tetapi metode ini ollection View
terlihat persis sama dengan metode serupa untuk UIView biasa , tidak ada indexPath di sana , kami hanya perlu mengembalikan session.canLoadObjects (ofClass: UIImage.self) , dan itu berarti bahwa saya menerima "reset" objek dari cl ini PAS dalam koleksi saya ollection View
:
Tapi ini tidak cukup untuk "membuang" Drop
gambar ke koleksi saya Collection View
DI LUAR.Jika "reset" Drop
gambar berlangsung dalam koleksi Collection View
, pengguna akan mereorganisasi unsur sendiri item melalui mekanisme Drag & Drop
, maka hanya satu gambar UIImage , dan penerapan metode canHandle akan terlihat seperti dengan cara di atas.Tetapi jika "dumping" Drop
gambar terjadi DI LUAR, maka kita hanya boleh menangani "drag and drop" Drag
yang merupakan gambar UIImage bersama dengan URL
gambar ini, karena kita tidak akan menyimpan gambar UIImage secara langsungdalam Model. Dalam hal ini, saya hanya akan mengembalikan true dalam metode canHandle jika beberapa kondisi terpenuhi pada saat yang sama : session.canLoadObjects (ofClass: NSURL.self) && session.canLoadObjects (ofClass: UIImage.self) :
Saya harus menentukan apakah saya berurusan dengan "reset" DI LUAR ATAU DALAM. Saya akan melakukan ini dengan menggunakan konstanta isSelf yang dihitung , untuk perhitungan yang saya dapat menggunakan hal itu dalam Drop
sesi sesi sebagai Drag
sesi lokal localDragSession . Dalam lokal ini Drag
sesi, pada gilirannya, adalah konteks lokal localContext .Jika Anda ingat, kami menetapkan konteks lokal ini dalam metode iniitemsForVeginning Drag
delegasi UICollectionViewDragDelegate :
Aku akan menjelajahi konteks lokal localContext untuk kesetaraan dalam koleksi saya CollectionView . Benar, TYPE localContext akan menjadi Any , dan saya perlu melakukan casting TYPE Any menggunakan operator as? UICollectionView :
Jika konteks lokal (session.localDragSession .localContext sebagai UICollectionView ??) Apakah koleksi saya CollectionView , variabel diperluas isSelf adalah benardan ada "reset" lokal DI DALAM koleksi saya. Jika persamaan ini dilanggar, maka kita berurusan dengan "reset" Drop
DI LUAR.Metode canHandle melaporkan bahwa kami hanya dapat menangani "seret dan lepas" Drag
ke koleksi kami Collection View
. Kalau tidak, lebih jauh, tidak masuk akal untuk berbicara tentang "dumping Drop
. "Jika kita terus "ulang" Drop
, masih sampai saat ini bahwa pengguna akan mengangkat jari Anda dari layar dan akan ada nyata "ulang" Drop
, kita harus melaporkan iOS
menggunakan metode dropSessionDidUpdate delegasi UICollectionViewDropDelegate tentang tawaran kami UIDropProposal untuk melaksanakan ulang Drop
.Dalam metode ini, kita harus mengembalikan Drop
kalimat yang dapat memiliki nilai .copy atau .move atau .cancel atau .forbidden untuk argumen operasi . Dan ini semua kemungkinan yang kita miliki dalam kasus biasa ketika berhadapan dengan UIView biasa .Tetapi koleksi Collection View
berjalan lebih jauh dan menawarkan untuk mengembalikan penawaran khusus UICollectionViewDropProposal , yang merupakan subclass
kelas dari UIDropProposal dan memungkinkan Anda untuk menentukan, selain operasi operasi, parameter maksud tambahan untuk koleksi Collection View
.Parameterintent memberi tahu koleksiCollection View
apakah kita ingin elemen "dibuang" ditempatkan di dalam sel yang ada , atau apakah kita ingin menambahkan sel baru . Lihat perbedaannya? Dalam hal pengumpulan,Collection View
kita harus menyampaikan maksud niat kita .Dalam kasus kami, kami selalu ingin menambahkan sel baru, jadi Anda akan melihat seperti apa parameter maksud kami.Kami memilih konstruktor kedua untuk UICollectionViewDropProposal :
Dalam kasus kami, kami selalu ingin menambahkan sel baru dan parameter maksud akan mengambil nilai .insertAtDestinationIndexPath sebagai lawan.insertIntoDestinationIndexPath .
Saya kembali menggunakan konstanta yang dihitung itu sendiri , dan jika itu adalahreorganisasi sendiri , maka saya akan pindah. Pindah , kalau tidak saya menyalin .copy . Dalam kedua kasus, kami menggunakan .insertAtDestinationIndexPath , yaitu memasukkan sel baru.Sejauh ini saya belum menerapkan metode performDrop , tetapi mari kita lihat apa yang sudah dapat dilakukan koleksiCollection View
dengan sepotong kecil informasi yang kami sediakan untuk itu.Saya seret gambar dariSafari
mesin pencariGoogle
, dan tanda "+" berwarna hijau muncul di atas gambar ini, menunjukkan bahwa Galeri Gambar kami tidak hanya siap untuk menerima dan menyalin gambar ini bersamanya URL
, tetapi juga menyediakan tempat di dalam koleksi Collection View
:
saya dapat mengklik sepasang gambar lain di Safari
, dan Sudah akan ada 3 gambar "diseret":
Tetapi jika saya mengangkat jari saya dan "menjatuhkan" Drop
gambar-gambar ini, mereka tidak akan ditempatkan di Galeri kami, tetapi hanya kembali ke tempat sebelumnya, karena kami belum menerapkan metode performDrop .
Anda bisa melihat bahwa koleksinya Collection View
sudah tahu apa yang ingin saya lakukan.Koleksinya Collection View
benar-benar luar biasa untuk mekanisme ini.Drag & Drop
, dia memiliki fungsionalitas yang sangat kuat untuk ini. Kami nyaris tidak menyentuhnya dengan menulis 4 baris kode, dan dia sudah bergerak cukup jauh dalam persepsi "reset" Drop
.Mari kita kembali ke kode dan mengimplementasikan metode performDrop .
Dalam metode ini, kita tidak akan dapat bertahan dengan 4 baris kode, karena metode performDrop sedikit lebih rumit, tetapi tidak terlalu banyak.Ketika "reset" terjadi Drop
, dalam metode performDrop kita perlu memperbarui Model kita, yang merupakan galeri gambar imageGallery dengan daftar gambar gambar , dan kita perlu memperbarui koleksi koleksi visual kita .Kami memiliki dua skenario "reset" yang berbeda Drop
.Jika ada "reset" Drop
dari koleksi collectionView saya , maka saya harus "mengatur ulang" Drop
elemen koleksi di tempat baru dan menghapusnya dari tempat lama, karena dalam hal ini saya memindahkan ( .move ) elemen koleksi ini. Ini adalah tugas yang sepele.Ada "reset" Drop
dari aplikasi lain, maka kita harus menggunakan properti itemProvider dari elemen item "diseret" untuk memilih data.Saat kami melakukan "reset" Drop
di koleksi collectionView , koleksi memberi kami koordinator koordinator. Pertama dan terpenting, kami melaporkan koordinator koordinator , itu destinationIndexPath , yaitu indexPath "-destination", "ulang" Drop
, itu adalah di mana kita akan "ulang."
Tapi destinationIndexPath mungkin nihil , karena Anda dapat menyeret gambar "dibuang" ke bagian koleksi Collection View
yang bukan tempat di antara beberapa sel yang ada , sehingga bisa jadi nihil . Jika situasi ini terjadi, maka saya membuat IndexPath dengan elemen item ke-0 di bagian bagian ke-0 .
Saya bisa memilih indexPath lainnya , tetapi saya akan menggunakan indexPath ini secara default.Sekarang kita tahu di mana kita akan melakukan "reset" Drop
. Kita harus melalui semua koordinator "reset". Item yang disediakan oleh koordinator . Setiap item dari daftar ini memiliki TYPE UICollectionViewDropItem dan dapat memberikan kami informasi yang sangat menarik.Sebagai contoh, jika saya bisa mendapatkan sourceIndexPath dari item.sourceIndexPath , saya akan tahu persis apa itu "drag and drop" Drag
dilakukan dengan sendirinya, diri, dan sumber seret Drag
adalah item koleksi dengan indexPath sama dengan sourceIndexPath :
Saya bahkan tidak perlu melihat localContext dalam hal ini untuk mengetahui bahwa "seret dan lepas" ini dilakukan DI DALAM koleksi collectionView . Wow!
Sekarang saya tahu sumber sourceIndexPath dan "tujuan" destinationIndexPath Drag & Drop
, dan tugas menjadi sepele. Yang perlu saya lakukan adalah memperbarui Model sehingga sumber dan "tujuan" ditukar, dan kemudian memperbarui koleksi collectionView , di mana Anda harus menghapus item koleksi dengan sourceIndexPath dan menambahkannya ke koleksi dengan destinationIndexPath .Kasus lokal kami adalah yang paling sederhana, karena dalam kasus ini mekanismenya Drag & Drop
bekerja tidak hanya dalam aplikasi yang sama, tetapi dalam koleksi collectionView yang sama , dan saya bisa mendapatkan semua informasi yang diperlukan menggunakan koordinator koordinator. Mari kita implementasikan dalam kasus lokal paling sederhana ini:
Dalam kasus kami, saya bahkan tidak perlu localObject , yang saya "sembunyikan" sebelumnya ketika saya membuat dragItem dan yang sekarang saya pinjam dari item "diseret" dalam koleksi item dalam item form.localObject . Kita akan membutuhkannya saat "membuang" Drop
gambar ke "tempat sampah," yang ada di aplikasi yang sama, tetapi bukan koleksi collectionView yang sama . Dua IndexPathes sudah cukup bagi saya sekarang : sumber sourceIndexPath dan "tujuan" destinationIndexPath .Saya mendapat informasi terlebih dahuluimageInfo tentang gambar di tempat lama dari Model, menghapusnya dari sana. Dan kemudian masukkan ke array gambar model saya imageGallery informasi imageInfo gambar dengan indeks baru destinationIndexPath.item . Ini adalah bagaimana saya memperbarui Model saya:
Sekarang saya harus memperbarui koleksi collectionView itu sendiri. Sangat penting untuk memahami bahwa saya tidak ingin membebani semua data di collectionViewView sayadengan reloadData () di tengah proses "seret dan lepas"Drag
, karena ini menginstal ulang seluruh "Dunia" Galeri Gambar kami, yang sangat buruk, JANGAN LAKUKAN. Sebagai gantinya, saya akan membereskan dan memasukkan elemenitem secara individual:
Saya menghapus item koleksi collectionView dengan sourceIndexPath dan memasukkan item koleksi baru dengan destinationIndexPath .Sepertinya kode ini berfungsi dengan baik, tetapi dalam kenyataannya, kode ini dapat "crash" aplikasi Anda. Alasannya adalah bahwa Anda membuat banyak perubahan pada koleksi collectionView Anda , dan dalam hal ini setiap langkah mengubah koleksi perlu disinkronkan dengan Model secara normal, yang tidak diamati dalam kasus kami, karena kami melakukan kedua operasi pada saat yang sama: hapus dan sisipkan. Oleh karena itu koleksi collectionViewakan berada di beberapa titik dalam kondisi TIDAK disinkronkan dengan Model.Tapi ada yang benar-benar keren cara untuk berkeliling itu, yaitu bahwa koleksi CollectionView memiliki metode bernama performBatchUpdates , yang memiliki sirkuit ( closure
) dan dalam rangkaian ini saya dapat menempatkan sejumlah tersebut deleteItems , insertItems , moveItems dan semua yang saya inginkan:
sekarang deleteItems dan insertItems akan dilakukan sebagai operasi tunggal, dan tidak akan pernah melihat kurangnya sinkronisasi model Anda dengan koleksi CollectionView .Dan akhirnya, hal terakhir yang perlu kita lakukan adalah meminta koordinator untuk mengimplementasikan dan menghidupkan "reset" itu sendiri Drop
:
Segera setelah Anda mengangkat jari Anda dari layar, gambar bergerak, semuanya terjadi pada saat yang sama: "reset", gambar menghilang di satu tempat dan penampilan di tempat lain.Mari kita coba untuk memindahkan gambar uji "Venesia" di Galeri Gambar kami ke akhir baris pertama ...
... dan "reset" itu:
Seperti yang kita inginkan, itu ditempatkan di akhir baris pertama.Hore!
Semuanya bekerja!Sekarang kita TIDAK akan berurusan dengan case lokal, yaitu, ketika elemen "reset" keluar, yaitu dari aplikasi lain.Untuk melakukan ini, kami menulis lagi dalam kode sehubungan dengan sourceIndexPath . Jika kita tidak memiliki sourceIndexPath , maka ini berarti bahwa elemen "reset" berasal dari LUAR dan kita harus menggunakan transfer data menggunakan itemProver dari item reset.dragItem.itemProvider elemen :
Jika Anda "drag Drag
and drop" OUTSIDE dan "drop" ”Drop
, lalu apakah informasi ini tersedia secara instan? Tidak, Anda memilih data dari hal "diseret" ASYNCHRONOUSLY. Tetapi bagaimana jika sampel membutuhkan waktu 10 detik? Apa yang akan dilakukan koleksi saat ini ollection View
? Selain itu, data mungkin tidak sampai dalam urutan yang kami minta. Untuk mengelola ini tidak mudah, dan Apple
mengusulkan untuk ollection View
kasus ini teknologi yang sama sekali baru untuk penggunaan pengganti Placeholders
.Anda menempatkan Collection View
placeholder dalam koleksi Anda Placeholder
, dan koleksi Collection View
mengatur semuanya untuk Anda, jadi yang harus Anda lakukan ketika data akhirnya dipilih adalah meminta placeholder untuk Placeholder
memanggil placeholder -nya konteks konteksdan katakan padanya bahwa Anda menerima informasi itu. Kemudian perbarui Model Anda dan konteks placeholderContext OTOMATIS bertukar sel dengan placeholder Placeholder
dengan salah satu sel Anda , yang sesuai dengan jenis data yang Anda terima.Kami melakukan semua tindakan ini dengan membuat konteks placeholderContext placeholder yang mengelola placeholder Placeholder
dan yang Anda dapatkan dari koordinator koordinator , meminta Anda untuk "mengatur ulang" Drop
elemen item ke placeholder Placeholder
.Saya akan menggunakan penginisialisasi untuk konteks placeholderContext placeholderbahwa “throws” dragItem ke UICollectionViewDropPlaceholder :
Objek yang akan saya “throw” Drop
adalah item.dragItem , di mana item adalah untuk elemen dari loop, karena kita dapat membuang Drop
banyak koordinator.items . Kami “melempar” mereka satu per satu. Jadi item.dragItem adalah apa yang kita “seret Drag
dan lepas ” Drop
. Argumen berikutnya untuk fungsi ini adalah placeholder, dan saya akan membuatnya menggunakan initializer UICollectionViewDropPlaceholder :
Untuk melakukan ini, saya perlu tahu DIMANA saya akan memasukkan placeholderPlaceholder
, mis. insertionIndexPath , serta pengidentifikasi reuseIdentifier sel yang digunakan kembali .Argumen insertionIndexPath jelas sama dengan destinationIndexPath , itu adalah IndexPath untuk menempatkan objek "diseret", itu dihitung pada awal metode performDropWith .Sekarang mari kita lihat id sel reuseIdentifier . Anda perlu memutuskan apa jenis sel sel merupakan tempat Anda Placeholder
. Koordinator koordinator tidak memiliki sel "pra- paket " untuk pelacakPlaceholder
. Bahwa Anda harus mengambil keputusan pada sel sel . Oleh karena itu, pengidentifikasi reuseIdentifiercell sel yang digunakan kembali diminta dari Anda storyboard
sehingga dapat digunakan sebagai PROTOTIPE.Saya akan menyebutnya "DropPlaceholderCell", tetapi pada dasarnya, saya bisa memberi nama apa saja.Ini hanya string String yang akan saya gunakan pada milik saya storyboard
untuk membuat hal ini.Kembali ke kami storyboard
dan buat sel sel untuk placeholder Placeholder
. Untuk melakukan ini, kita hanya perlu memilih koleksi Collection View
dan memeriksanya. Di bidang pertama, Items
saya berubah 1
menjadi2
. Ini segera membuat sel kedua bagi kami, yang merupakan salinan persis dari sel pertama.
Kami memilih sel baru kami ImageCell
, mengatur pengenal " DropPlaceholderCell
", menghapus semua UI
elemen dari sana , termasuk Image View
, karena PROTOTIPE ini digunakan ketika gambar belum tiba. Kami menambahkan indikator aktivitas baru dari Palet Objek di sana Activity Indicator
, itu akan berputar, membiarkan pengguna tahu bahwa saya mengharapkan beberapa "reset" data. Juga mengubah warna latar belakang Background
untuk memahami bahwa ketika "Reset" dari gambar luar bekerja sama persis sel ini sel sebagai prototipe:
Selain jenis sel baru tidak harus ImageCollectionVewCell, karena tidak akan ada gambar di dalamnya. Saya akan membuat sel ini sel TYPE UIollectionCiewCell normal , karena kita tidak memerlukan apa pun Outlets
untuk kontrol:
Mari kita mengkonfigurasi indikator aktivitas Activity Indicator
sehingga mulai menjiwai dari awal, dan saya tidak perlu menulis apa pun dalam kode untuk memulainya. Untuk melakukan ini, klik pada opsi Animating
:
Dan itu saja. Jadi, kami membuat semua pengaturan untuk sel ini DropPlaceholderCell
, kami kembali ke kode kami. Sekarang kami memiliki pencari lokasi yang hebat yang Placeholder
siap berangkat .Yang harus kita lakukan adalah mendapatkan data, dan ketika data diterima, kita cukup memberi tahu placeholderContext tentang konteks ini dan itu akan menukar placeholderPlaceholder
dan sel "asli" kami dengan data, dan kami akan membuat perubahan pada Model.Saya akan "memuat" SATU objek, yang akan menjadi item saya menggunakan metode loadObject (ofClass: UIImage.self) (tunggal). Saya menggunakan kode item.dragItem.itemProvider pemasok itemProvider , yang akan memberikan data elemen memiliki item yang asynchronous. Jelas bahwa jika iitemProvider terhubung , maka kita mendapatkan objek "reset" iitem di luar aplikasi ini. Berikut ini adalah metode loadObject (ofClass: UIImage.self) (tunggal):
Penutupan khusus ini TIDAK dilakukan padamain queue
. Dan, sayangnya, kami harus beralih main queue
menggunakan DispatchQueue.main.async {} untuk "menangkap" rasio aspek gambar di variabel aspekRasio lokal .Kami benar-benar telah memperkenalkan dua variabel lokal imageurl dan aspectRatio ...
... dan akan "menangkap" mereka dengan meng-upload gambar gambar dan URL url No :
Jika dua variabel lokal imageurl dan aspectRatio tidak sama untuk nihil , kami akan meminta konteks sebuah tempat placeholderSontext menggunakan metode commitInsertionberi kami kesempatan untuk mengubah Model imageGallery kami :
Dalam ungkapan ini, kami memiliki insertionIndexPath - ini adalah indexPath untuk dimasukkan, dan kami mengubah Model imageGallery kami . Hanya itu yang perlu kita lakukan, dan metode ini secara OTOMATIS akan menggantikan placeholder Placeholder
dengan sel dengan memanggil metode normal cellForItemAt .Perhatikan bahwa insertionIndexPath bisa sangat berbeda dari destinationIndexPath . Mengapa
Karena pengambilan sampel data mungkin memakan waktu 10 detik, tentu saja, itu tidak mungkin, tetapi mungkin butuh 10 detik. Selama ini, Collection View
banyak yang bisa terjadi dalam koleksi . Dapat menambahkan sel-sel baru sel , semuanya terjadi cukup cepat.SELALU menggunakan insertionIndexPath , dan HANYA insertionIndexPath , untuk memperbarui Model Anda.Bagaimana kami memperbarui model kami?Kami akan menyisipkan struktur imageModel ke dalam array imageGallery.images , terdiri dari rasio aspek dari gambar aspectRatio dan URL gambar imageURL yang dikembalikan oleh penyedia terkait kepada kami .Ini memperbarui Model imageGallery kami , dan metode commitInsertion melakukan sisanya untuk kami. Anda tidak perlu lagi melakukan sesuatu yang ekstra, tidak ada sisipan, tidak ada penghapusan, semua ini. Dan, tentu saja, karena kita berada dalam penutupan, kita perlu menambahkan diri. .
Jika kita untuk beberapa alasan tidak bisa mendapatkan rasio aspek aspectRatio dan URL
gambar imageurl dari yang sesuai dengan penyedia , kesalahan mungkin telah diterima kesalahan bukan oleh penyedia , kita harus membiarkan mereka tahu konteks placeholderContext , Anda perlu untuk menghancurkan ini pengganti Placeholder
, karena kita semua sama, kita tidak bisa dapatkan data lain:
Satu hal yang perlu diingat adalah URLs
bahwa mereka datang dari tempat-tempat seperti ini Google
, dalam kenyataannya, mereka membutuhkan transformasi kecil untuk mendapatkan "bersih"URL
untuk gambar. Bagaimana masalah ini diselesaikan dapat dilihat pada aplikasi demo ini dalam file Utilities.swift
di Github .Karenanya, ketika menerima URL
gambar, kami menggunakan properti imageURL dari kelas URL :
Dan itu semua yang perlu Anda lakukan untuk menerima di luar sesuatu di dalam koleksi Collection View
.Mari kita lihat dalam aksi. Kami secara bersamaan meluncurkan dalam mode multitasking aplikasi demo kami ImageGallery
dan Safari
dengan mesin pencari Google
. Di Google
kami sedang mencari gambar pada tema "Dawn" (matahari terbit). The Safari
sudah dibangunDrag & Drop
mekanismenya, jadi kita dapat memilih salah satu gambar ini, tahan untuk waktu yang lama, gerakkan sedikit dan seret ke Galeri Gambar kami.
Kehadiran tanda tambah hijau "+" menunjukkan bahwa aplikasi kami siap menerima gambar pihak ketiga dan menyalinnya ke koleksi Anda di lokasi yang ditentukan oleh pengguna. Setelah kami "mengatur ulang", diperlukan waktu untuk mengunduh gambar, dan saat ini berfungsi Placeholder
:
Setelah unduhan selesai, gambar "reset" ditempatkan di tempat yang tepat, dan Placeholder
menghilang:
Kami dapat terus "mengatur ulang" gambar dan menempatkannya di koleksi lebih banyak gambar:
Setelah "reset" bekerja Placeholder
:
Sebagai hasilnya, Galeri Gambar kami dipenuhi dengan gambar baru:
Sekarang jelas bahwa kita mampu untuk mengambil gambar dari luar, kita tidak perlu gambar tes, dan kami akan menghapus:
kami viewDidLoad menjadi sangat sederhana: itu adalah kita lakukan kami Controller
Drag
dan Drop
mendelegasikan dan menambahkan gesture recognizer mencubit , yang mengatur jumlah gambar per baris:
Tentu saja , kita dapat menambahkan cache untuk gambar imageCache :
Kami akan mengisi imageCache ketika "reset" Drop
dalam metode performDrop ...
dan ketika mengambil dari "jaringan" di kelas ImageCollectionViewCell kustom :
Dan kita akan menggunakan cache imageCache saat memainkan selsel Galeri Gambar kami di kelas khusus ImageCollectionViewCell :
Sekarang kita mulai dengan koleksi kosong ...
... lalu "jatuhkan" gambar baru ke dalam koleksi kami ...
... gambar diunggah danPlaceholder
berfungsi ...
... dan gambar muncul di tempat yang tepat:
Kami terus mengisi koleksi kami DI LUAR:
Datang memuat gambar danPlaceholders
bekerja ...
Dan gambar muncul di tempat yang tepat:
Jadi, kita dapat melakukan banyak hal dengan Galeri Gambar kami: isi di luar, mengatur ulang item DI DALAM, berbagi gambar dengan aplikasi lain niyami.Kita hanya perlu mengajarinya cara menyingkirkan gambar yang tidak perlu dengan "mengatur ulang" gambar-gambar ituDrop
di "tempat sampah" yang disajikan di bilah navigasi di sebelah kanan. Seperti yang dijelaskan di bagian "Fitur Aplikasi Demo Galeri Gambar", "tempat sampah" diwakili oleh kelas GabageView , yang diwarisi dari UIView dan kita harus mengajarkannya untuk menerima gambar dari koleksi kami ollection View
.Atur ulang Drop
gambar Galeri ke tempat sampah.
Segera dari tempat itu - ke tambang. Aku akan menambah GabageView "interaksi" Interaksi dan akan UIDropInteraction , karena saya sedang mencoba untuk "reset" Drop
beberapa hal. Yang kita butuhkan untuk memberikan UIDropInteraction ini adalah delegasi delegasi , dan saya akan menugaskan diri saya sendiri , kepada delegasi delegasi ini :
Secara alami, kelas GabageView kami harus mengonfirmasi bahwa kami menerapkan protokol UIDropInteractionDelegate :
Semua yang perlu kita lakukan untuk membuatnya berfungsi Drop
, ini untuk mengimplementasikan metode canHandle yang sudah kita kenal ,sessionDidUpdate dan performDrop .
Namun, tidak seperti metode serupa untuk pengumpulanCollection View
, kami tidak memiliki informasi tambahan dalam bentuk indexPath tempat pembuangan.Mari kita implementasikan metode ini.Dalam metode canHandle akan diproses hanya "drag and drop"Drag
, yang mewakili citra UIImage . Oleh karena itu, sayahanyaakan mengembalikan true jika session.canLoadObjects (ofClass: UIImage.self) :
Dalam metode canHandle ,Anda pada dasarnya hanya mengatakan bahwa jika objek "draggable" bukan gambar UIImage, kemudian lebih lanjut tidak masuk akal untuk melanjutkan "reset" Drop dan memanggil metode selanjutnya.Jika objek "draggable" adalah gambar UIImage , maka kami akan menjalankan metode sessionDidUpdate . Yang perlu kita lakukan dalam metode ini adalah mengembalikan tawaran "reset" UIDropProposal kami Drop
. Dan saya siap untuk hanya menerima objek "diseret" LOCALLY dari UIImage TYPE gambar , yang dapat "dijatuhkan" di Drop
mana saja di dalam GarbageView saya . GarbageView saya tidak akan berinteraksi dengan gambar yang dibuang ke luar. Jadi saya parsing menggunakan variabel session.localDragSessionapakah ada "reset" lokal Drop
, dan saya mengembalikan kalimat "reset" dalam bentuk konstruktor UIDropProposal dengan argumen operasi mengambil nilai .copy , karena SELALU LOKAL "seret dan lepas" Drag
di aplikasi saya akan berasal dari koleksi Collection View
. Jika "seret Drag
dan lepas" dan "reset" Drop
terjadi DI LUAR, maka saya mengembalikan kalimat "reset" dalam bentuk konstruktor UIDropProposal dengan argumen operasi yang mengambil nilai .fobbiden , yaitu, "terlarang" dan kami akan menerima tanda larangan "reset" alih-alih tanda hijau .
Menyalin Gambar UIImage, kami akan mensimulasikan penurunan skalanya hingga hampir 0, dan ketika "reset" terjadi, kami akan menghapus gambar ini dari koleksi Collection View
.Untuk membuat ilusi "dumping and menghilang" gambar di "tempat sampah" untuk pengguna, kami menggunakan metode previewForDropping , baru bagi kami , yang memungkinkan kita untuk mengarahkan "dumping" Drop
ke tempat lain dan pada saat yang sama mengubah objek "dibuang" selama animasi:
Dalam metode ini, menggunakan penginisialisasi UIDragPreviewTarget, kami mendapatkan preView baru untuk objek target yang akan dihapus dan mengarahkannya menggunakan metode retargetedPreviewke tempat baru, ke "tempat sampah", dengan skalanya turun ke hampir nol:
Jika pengguna mengangkat jarinya, maka "reset" terjadi Drop
, dan saya (seperti GarbageView ) menerima pesan performDrop . Dalam pesan performDrop, kami melakukan "reset" yang sebenarnya Drop
. Sejujurnya, gambar yang dibuang ke GarbageView sendiri tidak lagi menarik bagi kami, karena kami akan membuatnya tidak terlihat, kemungkinan besar fakta penyelesaian "reset" Drop
akan memberi sinyal bahwa kami menghapus gambar ini dari koleksi Collection View
. Untuk mencapai ini, kita harus tahu koleksi itu sendiri dan koleksi indexPathgambar yang dibuang di dalamnya. Dari mana kita bisa mendapatkannya?Karena proses Drag & Drop
berlangsung dalam satu aplikasi, itu tersedia bagi kita semua lokal: lokal Drag
sesi localDragSession kami Drop
sesi sesi , konteks lokal localContext , yang merupakan koleksi kami sollectionView dan objek lokal localObject , yang bisa kita lakukan dengan sendirinya ulang gambar gambar dari "Galeri" atau indexPath . Karena ini kita bisa mendapatkan dalam metode performDrop kelas GarbageView koleksi koleksi , dan menggunakannyaDataSource bagaimana ImageGalleryCollectionViewController dan Model imageGallery kamiController
, kita bisa mendapatkan berbagai gambar gambar TYPE [ImageModel]:
Dengan bantuan lokalDrag
sesi localDragSession kamiDrop
sesi sesi kami mampu mendapatkan semua "drag" pada GarbageView Drag
unsur item , dan mungkin ada banyak, seperti yang kita tahu, dan semuanya adalah gambar koleksi collectionView kami . MembuatDrag
elemen dragItems koleksi kamiCollection View
, kami telah menyediakan untuk setiap "seret"Drag
elemendragItem objek lokal localObject , yang adalah gambaran dari gambar , tetapi kita tidak berguna selama pengumpulan reorganisasi internal CollectionView , tetapi "reset" Gambar Galeri "sampah dapat" kita sangat membutuhkan di fasilitas lokal localObject "drag" objek dragItem , setelah sekian lama kami tidak memiliki koordinator , yang dengan murah hati membagikan informasi tentang apa yang terjadi dalam koleksi collectionView . Oleh karena itu, kami ingin objek lokal localObject indeks indexPath dalam gambar berbagai gambar model kamiimageGallery . Membuat perubahan yang diperlukan dalam metode dragItems (di indexPath: indexPath) kelas ImageGalleryCollectionViewController :
Sekarang kita bisa mengambil setiap "pretaskivaemogo" elemen barang itu localObject , yang merupakan indeks indexPath dalam gambar berbagai gambar model kami imagegallery , dan mengirimkannya ke indeks array indeks dan array indexPahes dari gambar yang dihapus:
Mengetahui array dari indexes index dan array dari indexPahes dari gambar yang dihapus, dalam metode performBatchUpdateskoleksi koleksi kita menghapus semua gambar dihapus dari Model gambar dan dari koleksi koleksi :
Jalankan aplikasi, mengisi galeri dengan gambar baru:
Pilih sepasang gambar yang kita ingin menghapus dari galeri kami ...
... "membuang" mereka pada ikon dengan "sampah" ...
Mereka berkurang hampir ke 0 ...
... dan menghilang dari koleksi Collection View
, bersembunyi di "tempat sampah":
Menyimpan gambar di antara mulai.
Untuk menyimpan Galeri Gambar di antara berjalan, kami akan menggunakan UserDefaults , setelah mengubah Model kami ke dalam JSON
format. Untuk melakukan hal ini, kita akan menambah kita Controller
variabel defailts var ...
..., dan dalam model ImageGallery dan ImageModel protokol Codable :
String String , array Array , URL , dan ganda sudah menerapkan protokol Codable , jadi kita tidak punya apa-apa lagi yang harus dilakukan untuk mendapatkan encoding kerja dan decoding ke Model ImageGallery dalam JSON
format.Bagaimana cara mendapatkan JSON
versi ImageGallery ?Untuk membuat ini diperluas variabel json var , yang mengembalikan hasil upaya untuk mengubah diri, diri , melalui JSONEncoder.encode () dalam JSON
format yang:
Dan itu semua. Entah Data akan dikembalikan sebagai hasil dari konversi diri ke format JSON
, atau nihil jika konversi ini gagal, meskipun yang terakhir tidak pernah terjadi, karena JENIS ini adalah 100% Dienkripsi . Digunakan Opsional variabel json hanya untuk alasan simetri.Sekarang kami memiliki cara untuk mengonversi Model ImageGallery ke format DataJSON
. Apakah variabel json memiliki Data TYPE ? yang dapat diingat di UserDefaults .Sekarang bayangkan bahwa entah bagaimana kami berhasil mendapatkan JSON
data json , dan saya ingin membuat ulang dari mereka Model kami, sebuah instance dari struktur ImageGallery . Untuk melakukan ini, sangat mudah untuk menulis INITIALIZER untuk ImageGallery , yang argumen inputnya adalah JSON
data json . Inisialisasi ini akan menjadi inisialisasi "jatuh" (failable
) Jika gagal diinisialisasi, maka crash dan kembali nihil :
Saya baru saja mendapatkan nilai newValue menggunakan decoder JSONDecoder , mencoba untuk men-decode data json yang diteruskan ke initializer saya, dan kemudian menetapkan sendiri .Jika saya berhasil melakukan ini, maka saya mendapatkan contoh baru dari ImageGallery , tetapi jika upaya saya gagal, maka saya mengembalikan nol , karena inisialisasi saya "gagal".Saya harus mengatakan bahwa di sini kita memiliki lebih banyak alasan untuk "gagal" ( fail
), karena sangat mungkin bahwa JSON
data jsondapat rusak atau kosong, semua ini dapat menyebabkan "jatuh" ( fail
) dari inisialisasi.Sekarang kita dapat menerapkan BACA JSON
data dan model pemulihan imagegallery metode viewWillAppear kami Controller
...
... serta entri dalam pengamat didSet {} sifat imagegallery :
Mari menjalankan aplikasi dan mengisi galeri kami gambar:
Jika kita menutup aplikasi dan buka lagi, kita dapat melihat galeri kami sebelumnya Gambar disimpan dalam UserDefaults .Kesimpulan
Artikel ini menunjukkan betapa mudahnya untuk mengintegrasikan teknologi Drag & Drop
ke dalam iOS
aplikasi menggunakan contoh aplikasi demo yang sangat sederhana "Galeri Gambar" . Ini memungkinkan untuk sepenuhnya mengedit Galeri Gambar, "melempar" gambar baru dari aplikasi lain di sana, memindahkan yang sudah ada dan menghapus yang tidak perlu. Dan juga untuk mendistribusikan gambar yang terakumulasi di Galeri ke aplikasi lain.Tentu saja, kami ingin membuat banyak koleksi gambar tematik yang indah seperti itu dan menyimpannya langsung ke iPad atau iCloud Drive. Ini dapat dilakukan jika masing-masing Galeri tersebut ditafsirkan sebagai UIDocument yang disimpan secara permanen. Interpretasi seperti itu akan memungkinkan kita untuk naik ke tingkat abstraksi berikutnya dan membuat aplikasi yang bekerja dengan dokumen. Dalam aplikasi seperti itu, dokumen Anda akan diperlihatkan oleh komponen DocumentBrowserViewController , sangat mirip dengan aplikasi tersebut Files
. Ini akan memungkinkan Anda untuk membuat gambar UIDocument dari jenis "Galeri Gambar" pada Anda iPad
dan pada iCloud Drive
, serta memilih dokumen yang diinginkan untuk dilihat dan diedit.Tapi ini adalah subjek dari artikel selanjutnya.PS Kode aplikasi demo sebelum implementasi mekanisme Drag & Drop
dan setelahnya ada di Github .