
تُعد آلية
Drag & Drop
تعمل على
iOS 11
و
iOS 12
طريقة لنسخ البيانات أو نقلها بشكل غير متزامن داخل تطبيق واحد وبين التطبيقات المختلفة. على الرغم من أن هذه التكنولوجيا عمرها 30 عامًا تقريبًا ، فقد أصبحت حرفيا تقنية "اختراق" على
iOS
نظرًا لحقيقة أنه عند سحب شيء ما على
iOS
، يتيح لك
multitouch
التفاعل بحرية مع بقية النظام وجمع البيانات لإعادة التعيين من تطبيقات مختلفة.
يجعل
iOS
من الممكن التقاط عناصر متعددة في وقت واحد. علاوة على ذلك ، لا يجب أن تكون في سهولة الوصول للاختيار: يمكنك أخذ الكائن الأول ، ثم الانتقال إلى تطبيق آخر والحصول على شيء آخر - سيتم جمع جميع الأشياء في "كومة" تحت إصبعك. ثم اتصل بالرصيف العالمي على الشاشة ، افتح أي تطبيق هناك والتقط الكائن الثالث ، ثم انتقل إلى الشاشة مع تشغيل التطبيقات ، ودون تحرير الكائنات ، قم بتفريغها في أحد البرامج المفتوحة. هذه الحرية في العمل ممكنة على
iPad
، على
iPhone
، وتقتصر تغطية
Drag & Drop
في
iOS
على إطار تطبيق واحد.
تحتوي التطبيقات الأكثر شيوعًا (
IbisPaint X
و
Chrome
و
IbisPaint X
و
Mail
و
Photos
و
Files
وما إلى ذلك) على آلية
Drag & Drop
. بالإضافة إلى ذلك ، زودت
Apple
المطورين
API
بسيطة للغاية وبديهية لتضمين آلية
Drag & Drop
في تطبيقك. تعمل آلية
Drag & Drop
، تمامًا مثل الإيماءات ، على
UIView وتستخدم مفهوم
التفاعلات ، مثل الإيماءات قليلاً ، لذلك يمكنك التفكير في آلية
Drag & Drop
ببساطة كإيماءة قوية حقًا.
من السهل جدًا الاندماج في تطبيقك ، بالإضافة إلى الإيماءات. خاصة إذا كان تطبيقك يستخدم جدول
UITableView أو مجموعة
UICollectionView ، لأنه بالنسبة لهم
API Drag & Drop
تحسين
API Drag & Drop
ورفعه إلى مستوى أعلى من التجريد بمعنى أن
Collection View
نفسها تساعدك في
فهرسة مسار عنصر المجموعة الذي تريد سحبه وإفلاته
Drag
. إنها تعرف مكان إصبعك وتفسره على أنه
indexPath لعنصر المجموعة الذي تقوم "بسحبه"
Drag
في الوقت الحالي ، أو باعتباره
indexPath لعنصر المجموعة حيث "تسقط"
Drop
شيء ما. لذا فإن
Collection View
تمدك بـ
indexPath ، ولكن بخلاف ذلك ، فهي تمامًا نفس
API Drag & Drop
UIView العادية.
تتكون عملية
Drag & Drop
على
iOS
4 مراحل مختلفة:
ارفع
الرفع (الرفع) - يحدث هذا عندما يقوم المستخدم بإشارة
ضغط طويلة ، مشيرًا إلى العنصر الذي سيكون "سحب وإسقاط". في هذه اللحظة ، يتم تشكيل ما يسمى بـ "
lift preview
" خفيفة الوزن للغاية للعنصر المشار إليه ، ثم يبدأ المستخدم في سحب أصابعه.

اسحب (اسحب واسقط)
السحب (السحب والإفلات) - هذا عندما يقوم المستخدم بتحريك الكائن على سطح الشاشة. خلال هذه المرحلة ، يمكن تعديل "
lift preview
" لهذا الكائن (تظهر علامة "+" خضراء أو علامة أخرى) ...

... يُسمح أيضًا ببعض التفاعل مع النظام: يمكنك النقر فوق كائن آخر وإضافته إلى جلسة "السحب والإفلات" الحالية:

إسقاط
يحدث
الانخفاض عندما يرفع المستخدم إصبعه. في هذه اللحظة ، يمكن أن يحدث شيئان: إما أن يتم تدمير كائن
Drag
، أو سيتم "إسقاط" كائن
Drop
في الوجهة.

نقل البيانات
إذا لم يتم إلغاء عملية
السحب والإفلات وحدثت إعادة تعيين "
الإفلات " ،
فسيحدث نقل البيانات (نقل البيانات) ، حيث تطلب "نقطة الإفلات" البيانات من "المصدر" ، ويحدث نقل البيانات غير المتزامن.
في هذا البرنامج التعليمي ، سنوضح لك كيفية
دمج آلية
Drag & Drop
بسهولة في تطبيق
iOS
الخاص بك باستخدام تطبيق العرض التجريبي للصور ، المأخوذ
من دورة الواجبات المنزلية CS193P في ستانفورد .
سنمنح
Collection View
القدرة على ملء أنفسنا بالصور في الخارج ، بالإضافة إلى إعادة تنظيم عناصر INSIDE باستخدام آلية
Drag & Drop
. بالإضافة إلى ذلك ، سيتم استخدام هذه الآلية لإلقاء العناصر غير الضرورية في "
Collection View
في "سلة المهملات" ، وهي
UIView عادية ويتم تمثيلها بواسطة زر في لوحة التنقل. يمكننا أيضًا مشاركة الصور التي تم جمعها في معرضنا مع تطبيقات أخرى باستخدام آلية
Drag & Drop
، على سبيل المثال ،
Notes
أو
Notes
أو
Mail
أو مكتبة صور (
Photo
).
ولكن قبل التركيز على تنفيذ آلية
Drag & Drop
في تطبيق العرض التوضيحي "معرض الصور" ، سوف أتناول مكوناتها الرئيسية بإيجاز شديد.
ميزات التطبيق التجريبي "معرض الصور"
واجهة المستخدم (
UI
) لتطبيق معرض الصور بسيطة للغاية. هذا هو "مقتطف شاشة" من
Image Gallery Collection View Controller
المدرجة في
Navigation Controller
:

الجزء المركزي من التطبيق هو بالتأكيد
Image Gallery Collection View Controller
، والتي تدعمها فئة
ImageGalleryCollectionViewController مع نموذج معرض الصور كمتغير
var imageGallery = ImageGallery () :

يتم تمثيل النموذج من خلال
بنية ImageGallery الهيكلية التي تحتوي على مجموعة من صور
الصور ، حيث يتم وصف كل صورة ببنية هيكل
ImageModel التي تحتوي على
URL
لموقع الصورة (لن نقوم بتخزين الصورة نفسها) ونسبة العرض إلى الارتفاع:
يطبق ImageGalleryCollectionViewController بروتوكول
DataSource :

تحتوي الخلية المخصصة في مجموعة الخلايا على صورة
imageView: UIImageView! ومؤشر النشاط
الدوار: UIActivityIndicatorView! وهي مدعومة من
subclass
ImageCollectionViewCell من فئة
UICollectionViewCell :

Public API
لفئة
ImageCollectionViewCell هي
عنوان URL لصورة
imageURL . بمجرد تثبيته ،
UI
تحديث
UI
بنا ، أي يتم تحديد بيانات الصورة بشكل غير متزامن على هذه
الصورة URL وعرضها في الخلية. أثناء استرداد البيانات من الشبكة ، يعمل مؤشر نشاط
الدوار ، مما يشير إلى أننا في عملية استرداد البيانات.
أستخدم قائمة الانتظار العالمية
(qos: .userInitiated) مع
وسيطة جودة خدمة
qos للحصول على البيانات في
URL
معين ، والذي تم تعيينه على
.userInitiated لأنني حدد البيانات بناءً على طلب المستخدم:

في كل مرة تستخدم فيها المتغيرات الخاصة بك داخل إغلاق ، في حالتنا هي
imageView و
imageURL ، فإن المترجم
يجبرك على وضع
نفسك أمامها
. بحيث تسأل نفسك: "هل هناك" وصلة دورية للذاكرة "؟" ليس لدينا "
memory cycle
" صريحة هنا ، لأن
الذات نفسها ليس لديها مؤشر لهذا الإغلاق.
ومع ذلك ، في حالة تعدد مؤشرات الترابط ، يجب أن تضع في اعتبارك أن
الخلايا في
Collection View
قابلة لإعادة الاستخدام بفضل طريقة
dequeueReusableCell . في كل مرة تظهر فيها خلية (جديدة أو معاد استخدامها) على الشاشة ، يتم تنزيل الصورة من الشبكة بشكل غير متزامن (في هذا الوقت ، يدور "
الدوار " لمؤشر نشاط
الدوار ).
بمجرد اكتمال التنزيل واستلام الصورة ، يتم تحديث
UI
لخلية المجموعة هذه. لكننا لا ننتظر تحميل الصورة ، نواصل التمرير عبر المجموعة وخلافة المجموعة التي قمنا بوضع علامة عليها تغادر الشاشة دون تحديث
UI
بنا. ومع ذلك ، يجب أن تظهر صورة جديدة أدناه وسيتم إعادة استخدام نفس الخلية التي غادرت الشاشة ، ولكن لصورة أخرى ، والتي قد يتم تحميلها وتحديثها بسرعة. في هذا الوقت ، سيعود تحميل الصورة الذي بدأ سابقًا في هذه الخلية وسيتم تحديث الشاشة ، مما سيؤدي إلى نتيجة غير صحيحة. هذا لأننا ندير أشياء مختلفة تعمل مع الشبكة في مواضيع مختلفة. يعودون في أوقات مختلفة.
كيف نصلح الوضع؟
في إطار آلية
GCD التي نستخدمها ، لا يمكننا إلغاء تنزيل صورة الخلية التي غادرت الشاشة ، ولكن يمكننا ، عند
وصول بيانات
imageData من الشبكة ، التحقق من
URL
URL الذي تسبب في تحميل هذه البيانات ومقارنتها مع تلك التي يريدها المستخدم في هذه الخلية في الوقت الحالي ، أي
imageURL . إذا لم تتطابق ، فلن نقوم بتحديث خلية
UI
وننتظر بيانات الصورة التي نحتاجها:

هذا السطر الذي يبدو سخيفًا من
عنوان url للكود == self.imageURL يجعل كل شيء يعمل بشكل صحيح في بيئة متعددة الخيوط تتطلب خيالًا غير قياسي. والحقيقة هي أن بعض الأشياء في البرمجة متعددة الخيوط تحدث بترتيب مختلف عن كتابة الشفرة.
إذا لم يكن من الممكن تحديد بيانات الصورة ، يتم إنشاء صورة برسالة خطأ في شكل سلسلة "خطأ" ورمز تعبيري مع "عبوس". فقط المساحة الفارغة في
Collection View
قد تربك المستخدم قليلاً:

لا نريد للصورة التي تحتوي على رسالة الخطأ أن تكرر
الجانب النسبي لصورة الخطأ هذه ، لأنه في هذه الحالة سيتم تمديد النص مع الرمز التعبيري أو ضغطه. نود أن يكون مربعًا محايدًا ، أي أنه سيكون له نسبة عرض إلى ارتفاع قريبة من 1.0.

يجب أن نبلغ
مراقبنا بهذه الرغبة حتى يصحح نسبة العرض إلى الارتفاع بالنسبة
للفهرس المقابل في صورته
للمعرض النموذجي. هذه مهمة مثيرة للاهتمام ، وهناك العديد من الطرق لحلها ،
وسنختار أسهلها - باستخدام إغلاق
اختياري var closAspectRatio: (() -> Void)؟ . يمكن أن يساوي
صفرًا ولا يلزم تثبيته إذا لم يكن ذلك ضروريًا:

عند استدعاء الإغلاق
changeAspectRatio؟ () في حالة جلب بيانات خاطئة ، أستخدم السلسلة
الاختيارية . الآن يمكن لأي شخص مهتم في نوع ما من الإعدادات عند تلقي صورة خاطئة تعيين هذا الإغلاق على شيء محدد. وهذا بالضبط ما نقوم به في
Controller
بنا في طريقة
cellForItemAt :

يمكن العثور على التفاصيل
هنا .
لعرض الصور ذات الجانب الصحيح ، يتم
استخدام طريقة
sizeForItemAt لمفوض
UICollectionViewDelegateFlowLayout :

بالإضافة إلى
Collection View
صور
Collection View
، في
UI
لدينا
UI
قمنا بوضع
Bar Button
في لوحة التنقل مع صورة
GarbageView مخصصة تحتوي على "سلة المهملات" كعرض
فرعي :

في هذا الشكل ، يتم تغيير ألوان الخلفية لـ
GarbageView نفسه وزر
UIButton مع صورة "علبة القمامة" (توجد بالفعل خلفية شفافة) بحيث ترى أن المستخدم الذي "يفرغ" صور المعرض في "سلة المهملات" مساحة أكبر للمناورة عند إسقاط "
إسقاط " من مجرد رمز سلة المهملات.
تحتوي فئة
GarbageView على مُهيئان ويستخدمان أسلوب
الإعداد () :

في طريقة
الإعداد () ، أقوم أيضًا بإضافة
myButton كطريقة عرض
فرعية مع صورة "علبة القمامة" المأخوذة من
Bar Button
القياسي
Bar Button
:

قمت بتعيين خلفية شفافة لـ
GarbageView :

سيتم تحديد حجم
سلة المهملات وموقعها في طريقة
layoutSubviews () لفئة
UIView ، اعتمادًا على
حدود UIView المحددة:

هذا هو الإصدار الأولي من التطبيق التجريبي "معرض الصور" ، وهو موجود على
Github
في المجلد
ImageGallery_beginning
. إذا قمت بتشغيل هذا الإصدار من تطبيق "معرض الصور" ، فسترى نتيجة التطبيق الذي يعمل على بيانات الاختبار ، والتي سنحذفها لاحقًا وسنملأ "معرض الصور" حصريًا خارج:

خطة تنفيذ آلية
Drag & Drop
في تطبيقنا هي كما يلي:
- أولاً ، سنمنح
Collection View
بنا القدرة على "السحب" Drag
من الصور UIImage الخاصة به خارجياً ومحلياً ، - ثم سنقوم بتعليم مجموعتنا من صور
Collection View
لقبول Drag
"المسحوب" خارجيًا أو محليًا من UIImage ، - سنقوم أيضًا بتعليم GarbageView الخاص بنا باستخدام زر سلة المهملات لقبول صور UIImage التي تم سحبها من
Collection View
المحلي وإزالتها من Collection View
إذا ذهبت إلى نهاية هذا البرنامج التعليمي وأكملت جميع التغييرات الضرورية في التعليمات البرمجية ، فستتلقى الإصدار النهائي من التطبيق التجريبي "معرض الصور" ، والذي تم فيه تنفيذ آلية
Drag & Drop
. وهو موجود على
Github
في المجلد
ImageGallery_finished
.
يتم توفير أداء آلية
Drag & Drop
في
Collection View
خلال اثنين من المندوبين الجدد.
يتم تكوين أساليب
المندوب الأول ، سحب السحب ، لتهيئة وتخصيص السحب والإفلات.
طرق المفوض الثاني ،
dropDelegate ، تكمل السحب والإفلات من
Drags
، وتوفر ، في الأساس ،
Data transfer
البيانات وإعدادات الرسوم المتحركة المخصصة عند إعادة تعيين
Drop
، بالإضافة إلى أشياء أخرى مماثلة.
من المهم ملاحظة أن كلا هذين البروتوكولين مستقلان تمامًا. يمكنك استخدام بروتوكول أو بروتوكول آخر إذا كنت بحاجة فقط إلى "سحب"
Drag
أو "إسقاط"
Drop
، ولكن يمكنك استخدام كلا البروتوكولين في وقت واحد
Drag
وإفلات
Drag
والإسقاط في نفس الوقت ، مما يفتح وظائف إضافية آلية
Drag & Drop
لإعادة ترتيب العناصر في
Collection View
.
اسحب Drag
عناصر Drag
من Collection View
يعد تنفيذ بروتوكول
Drag
أمرًا بسيطًا للغاية ، وأول شيء يجب عليك فعله دائمًا هو تعيين نفسك ، نفسك ،
كمفوض السحب :

وبالطبع ، في أعلى فئة
ImageGalleryCollectionViewController ، يجب أن تقول "نعم" ،
ننفذ بروتوكول
UICollectionViewDragDelegate :

بمجرد أن نقوم بذلك ، يبدأ المترجم في "الشكوى" ،
وننقر على
الدائرة الحمراء ونطلب منك: "هل تريد إضافة الطرق المطلوبة لبروتوكول
UICollectionViewDragDelegate ؟"
أجبت: "بالطبع أريد!" وانقر على زر
Fix
:

الطريقة الوحيدة المطلوبة لبروتوكول
UICollectionViewDragDelegate هي طريقة
itemsForBeginning ، والتي ستخبر نظام
Drag
بأننا "نقوم بالسحب والإسقاط". يتم
استدعاء طريقة
itemsForBeginning عندما يبدأ المستخدم في "سحب" (
Dragging
) خلية في
خلية المجموعة.
لاحظ أنه في هذه الطريقة ، قامت
Collection View
بإضافة
indexPath . سيخبرنا هذا عن عنصر المجموعة ، أي
indexPath ، سنقوم "بالسحب والإسقاط". هذا حقًا مناسب جدًا لنا ، حيث أن التطبيق هو المسؤول عن استخدام وسيطات
الجلسة و
indexPath لمعرفة كيفية التعامل مع هذا السحب والإفلات من
Drag
.
إذا تم
إرجاع الصفيف
[UIDragItems] للعناصر "القابلة للسحب " ،
Drag
تهيئة "سحب"
Drag
؛ إذا
تم إرجاع الصفيف الفارغ
[] ، فسيتم تجاهل "سحب"
Drag
.
سوف أقوم بإنشاء
دالة dragItems صغيرة
خاصة (في: indexPath) مع وسيطة
indexPath . تقوم بإرجاع الصفيف
[UIDragItem] الذي
نحتاج إليه .
كيف يبدو شكل السحب والإفلات
UIDragItem ؟
لديه شيء واحد مهم للغاية يسمى
itemProvider .
itemProvider هو مجرد شيء يمكن أن يوفر البيانات التي سيتم سحبها.
ويحق لك أن تسأل: "ماذا عن" سحب وإسقاط "عنصر
UIDragItem الذي لا يحتوي على بيانات ببساطة؟" قد لا يحتوي العنصر الذي تريد سحبه على بيانات ، على سبيل المثال ، لأن إنشاء هذه البيانات عملية مكلفة. قد تكون هذه صورة
صورة أو شيء يتطلب تنزيل البيانات من الإنترنت. الشيء العظيم هو أن عملية
Drag & Drop
غير متزامنة تمامًا. عندما تبدأ في السحب والإسقاط ، يكون هذا الكائن خفيف الوزن للغاية (
lift preview
) ، تسحبه في كل مكان ولا يحدث أي شيء أثناء هذا "السحب". ولكن بمجرد "إسقاط"
Drop
ما في مكان ما ، فإن كونك عنصرًا في قائمة
العناصر ، يجب أن يوفر لك
العنصر "المسحوب " و "
المطرود " ببيانات حقيقية ، حتى إذا
استغرق الأمر قدرًا معينًا من الوقت.
لحسن الحظ ، هناك العديد من
موفري العناصر المضمنة . هذه هي الفئات الموجودة بالفعل في
iOS
وهي
itemPoviders ، على سبيل المثال ،
NSString
، والذي يسمح لك بسحب النص وإفلاته بدون خطوط. بالطبع ، هذه صورة
UIImage . يمكنك تحديد صور
UIImages وسحبها وإفلاتها
طوال الوقت . فئة
NSURL ، وهي رائعة تمامًا. يمكنك الذهاب إلى صفحة
Web
، وتحديد
URL
و "إسقاط" أينما تريد. يمكن أن يكون هذا رابطًا لمقالة أو
URL
لصورة ، كما سيكون في العرض التوضيحي الخاص بنا. هذه هي فئات الألوان من
UIColor ،
عنصر الخريطة
MKMapItem ، جهة اتصال
CNContact من دفتر العناوين ، يمكنك تحديد وسحب الكثير من الأشياء. كلهم من
itemProviders .
سنقوم بسحب وإسقاط صورة
UIImage . وهو موجود في خلية خلية
Collection View
مع
indexPath ، مما يساعدني في تحديد خلية
الخلية ،
وإخراج صورة Outlet
منها والحصول على
صورتها .
دعونا نعبر عن هذه الفكرة مع سطرين من التعليمات البرمجية.
أولاً ، أطلب
Collection View
مجموعتي حول
خلية لعنصر العنصر المقابل لهذا
indexPath .

تعمل طريقة
cellForItem (في: IndexPath) لطريقة Collection View
للخلايا المرئية فقط ، ولكنها بالطبع ستعمل في حالتنا ، لأنني "أسحب وأفلت" عنصر مجموعة
Drag
على الشاشة وهو مرئي.
لذا ، حصلت على خلية
خلية "قابلة للسحب".
بعد ذلك ، يمكنني استخدام عامل التشغيل
؟ لهذه الخلية بحيث تحتوي على نوع من
subclass
المخصصة الخاصة بي. وإذا كان هذا يعمل ، فأنا أحصل على صورة
Outlet
imageView ، والتي
ألتقط منها
صورتها . لقد قمت للتو "بالتقاط" صورة
الصورة لهذا
indexPath .
الآن بعد أن أصبحت لدي صورة
صورة ، كل ما علي فعله هو إنشاء واحدة من
عناصر UIDrag باستخدام عناصر الصورة الناتجة كعنصر
توفير ، أي الشيء الذي يزودنا بالبيانات.
يمكنني إنشاء
dragItem باستخدام مُنشئ
UIDragItem ، الذي يأخذ
itemProvider كوسيطة:

ثم نقوم بإنشاء
itemProvider لصورة
الصورة أيضًا باستخدام مُنشئ
NSItemProvider . هناك العديد من
منشئي NSItemProvider ، ولكن من بينهم واحد رائع حقًا -
NSItemProvider (الكائن: NSItemProviderWriting) :

يمكنك ببساطة إعطاء كائن
الكائن إلى مُنشئ
NSItemProvider ، وهو يعرف كيفية إخراج
itemProvider منه. على هذا النحو
، أعطي الصورة
الصورة التي تلقيتها من خلية
الخلية وأحصل على
itemProvider لـ
UIImage .
هذا كل ما في الأمر. أنشأنا
dragItem ويجب أن
نعيده كمصفوفة لها عنصر واحد.
ولكن قبل أن أعود dragItem ، أنا ذاهب لتفعل شيئا واحدا أكثر، ألا وهو تعيين متغير localObject ل dragItem ، أي ما يعادل الصورة الناتجة صورة .
ماذا يعني هذا؟
إذا قمت بإجراء "السحب والإفلات" Drag
محليًا ، أي داخل تطبيقك ، فلن تحتاج إلى مراجعة كل هذا الرمز المرتبط بـ itemProvider ، من خلال استرداد البيانات غير المتزامن. لا تحتاج للقيام بذلك ، كل ما عليك فعله هو استخدام localObject واستخدامه. هذا نوع من "الدائرة القصيرة" مع "السحب والإفلات" المحلي Drag
.ستعمل الشفرة التي كتبناها عند "السحب" Drag
خارج مجموعتنا Collection View
إلى تطبيقات أخرى ، ولكن إذا " سحبنا " Drag
محليًا ، يمكننا استخدام localObject . بعد ذلك ، أعيد مصفوفة من عنصر dragItem واحد .بالمناسبة ، إذا لم أتمكن لسبب ما من الحصول على صورة لخلية الخلية هذه ، فعندئذٍ أعيد صفيفًا فارغًا [] ، وهذا يعني إلغاء "السحب والإفلات" Drag
.
بالإضافة إلى وجوه المحلية localObject ، يمكنك أن نتذكر سياق محلي localContext لدينا Drag
جلسة الدورة . في حالتنا سيكون عبارة عن مجموعة من collectionView ومن المفيد لنا بعد ذلك:
وجود "السحب والإسقاط" Drag
، يمكنك إضافة المزيد من العناصر العناصر في هذا "السحب والإسقاط"، فقط تفعل لفتة الصنبور عليها. نتيجة لذلك ، يمكنك السحبDrag
العديد من العناصر في وقت واحد. وهذا سهل التطبيق مع طريقة تفويض أخرى ، UICollectionViewDragDelegate ، تشبه إلى حد كبير طريقة itemsForeginning ، وهي طريقة تسمى itemsForAddingTo . تبدو طريقة itemsForAddingTo مطابقة تمامًا لطريقة itemsForeginning وتعيد نفس الشيء تمامًا ، لأنها تعطينا أيضًا فهرسًا لما "قام المستخدم" بتسجيله أثناء عملية "السحب والإفلات" Drag
، وأحتاج فقط إلى الحصول على صورة الصورة من الخلية إلى التي قام المستخدم "بتسجيلها" وإعادتها.
إرجاع صفيف فارغ [] من أسلوب itemsForAddingToيؤدي إلى حقيقة أن إيماءة الصنبور سيتم تفسيرها بالطريقة المعتادة ، أي كخيار خلية الخلية هذه .وهذا كل ما نحتاجه للسحب والإسقاط Drag
.أطلقنا التطبيق.أختار صورة "البندقية" وأحتفظ بها لفترة وأبدأ في التحرك ...
ويمكننا حقًا سحب هذه الصورة إلى التطبيق Photos
، كما ترى علامة الجمع الخضراء "+" في الزاوية اليسرى العليا من الصورة "القابلة للسحب". يمكنني تنفيذ إيماءة نقر على صورة Artika أخرى من المجموعة Collection View
...
... والآن يمكننا إسقاط صورتين في التطبيق Photos
:
نظرًا لأن Photos
الآلية مضمنة بالفعل في التطبيقDrag & Drop
ثم كل شيء يعمل بشكل جيد وهو رائع.لذلك، وأنا أعمل "شد الحبل" Drag
و "إعادة تعيين" Drop
معرض الصورة لتطبيق آخر، لم يكن لدي الكثير لتفعله في طلبي، باستثناء تسليم صورة صورة كصفيف [UIDragItem] . هذه واحدة من العديد من الميزات الرائعة للآلية Drag & Drop
- من السهل جدًا جعلها تعمل في كلا الاتجاهين.إعادة تعيين Drop
الصور إلى المجموعةCollection View
الآن نحن بحاجة إلى إنشاء Drop
جزء لمجموعتي Collection View
حتى نتمكن من "تفريغ" Drop
أي صور "تم سحبها" داخل هذه المجموعة. يمكن أن تأتي "صورة قابلة للسحب" من الخارج ، أو مباشرة داخل هذه المجموعة.للقيام بذلك، ونحن نفعل نفس الشيء فعله لتفويض dragDelegate ، أي جعل أنفسنا، الذات ، تفويض dropDelegate في طريقة لviewDidLoad :
مرة أخرى، علينا أن يصعد إلى أعلى طبقتنا ImageGalleryCollectionViewController والتحقق من تنفيذ بروتوكول UICollectionViewDropDelegate :
بمجرد إضافة بروتوكولنا الجديد ، بدأ المترجم مرة أخرى في "الشكوى" من أننا لم ننفذ هذا البروتوكول. نضغط على الزر Fix
وتظهر الطرق المطلوبة لهذا البروتوكول أمامنا. في هذه الحالة ، يتم إعلامنا بأنه يجب علينا تنفيذ أسلوب PerformDrop :
يجب أن نقوم بذلك ، وإلا فلن تحدث "إعادة تعيين" Drop
. في الواقع ، سأقوم بتطبيق أسلوب PerformDrop أخيرًا ، لأن هناك Apple
طريقتان من الطرق الأخرى الموصى بها بشدة والتي تحتاج إلى تنفيذها Drop
للجزء. ومن canHandle و dropSessionDidUpdate :
إذا قمنا بتطبيق هاتين الطريقتين ، فيمكننا الحصول على علامة صغيرة خضراء بالإضافة إلى علامة "+" عندما نقوم بسحب صور من الخارج إلى مجموعتنا ollection View
، بالإضافة إلى ذلك ، لن يحاولوا التخلص مما لا نفهمه.دعنا نطبق canHandle . نسختك من طريقة canHandle ، وهي مخصصة للتجميع ollection View
، لكن هذه الطريقة ollection View
تبدو تمامًا مثل الطريقة المماثلة لـ UIView العادية ، ولا يوجد indexPath هناك ، نحتاج فقط إلى إرجاع session.canLoadObjects (ofClass: UIImage.self) ، وهذا يعني أن أقبل "إعادة تعيين" كائنات هذا cl PAS في مجموعتي ollection View
:
ولكن هذا لا يكفي "لإلقاء" Drop
الصورة في مجموعتي Collection View
خارج.واذا كان "إعادة تعيين" Drop
يأخذ صورة مكان داخل المجموعة Collection View
، وسيكون المستخدم إعادة تنظيم العناصر الخاصة من البنود من خلال آلية Drag & Drop
، ثم صورة واحدة فقط لUIImage ، وتطبيق الأسلوب canHandle سيبدو بالطريقة المذكورة أعلاه.ولكن إذا حدث "تفريغ" Drop
للصورة خارجًا ، فيجب علينا فقط التعامل مع "السحب والإفلات" Drag
وهي صورة UIImage جنبًا إلى جنب مع URL
هذه الصورة ، حيث لن نقوم بتخزين صور UIImage مباشرةفي النموذج. في هذه الحالة، سوف أعود صحيح في طريقة canHandle إلا إذا كان أداء كل الظروف بضع session.canLoadObjects (ofClass: NSURL.self) && session.canLoadObjects (ofClass: UIImage.self) :
لقد تركت لتحديد ما إذا أتعامل مع "إعادة تعيين" خارج أو داخل. سأفعل ذلك باستخدام الثابت المحسوب isSelf ، والذي يمكنني من خلاله حساب مثل هذا الشيء في Drop
جلسة جلسة مثل Drag
جلسة LocalDragSession للجلسة المحلية . هذه Drag
الجلسة المحلية بدورها لها سياق محلي localContext .إذا كنت تتذكر ، قمنا بتعيين هذا السياق المحلي في الطريقةitemsForVeginning Drag
مندوب UICollectionViewDragDelegate :
انا ذاهب لاستكشاف السياق المحلي localContext في المساواة في مجموعتي CollectionView . صحيح نوع localContext إلى أي ، ولست بحاجة لجعل "الصب" اكتب أي استخدام المشغل على النحو؟ UICollectionView :
إذا كان السياق المحلي (session.localDragSession .localContext كما UICollectionView؟) هل مجموعتي CollectionView ، المتغير توسيع يعتمد على طريقة التعلم غير صحيحوهناك "إعادة تعيين" محلية داخل مجموعتي. إذا تم انتهاك هذه المساواة ، فإننا نتعامل مع "إعادة تعيين" Drop
خارج. تفيدطريقة canHandle أنه يمكننا فقط التعامل مع هذا النوع من "السحب والإفلات" Drag
في مجموعتنا Collection View
. خلاف ذلك ، علاوة على ذلك ، لا معنى للحديث عن "الإغراق" Drop
.إذا واصلنا "إعادة تعيين" Drop
، فإنه لا يزال حتى اللحظة أن المستخدم سوف ترفع أصابعك خارج الشاشة وسوف يكون هناك "إعادة تعيين" حقيقية Drop
، علينا أن تقرير iOS
باستخدام طريقة dropSessionDidUpdate مندوب UICollectionViewDropDelegate عن عرضنا UIDropProposal لتنفيذ إعادة تعيين Drop
.في هذه الطريقة ، يجب أن نعيد Drop
جملة يمكن أن تحتوي على قيم .copy أو .move أو .cancel أو. ممنوع لوسيطة العملية . وهذه كلها الاحتمالات التي لدينا في الحالة العادية عند التعامل مع UIView العادي .لكن المجموعة Collection View
تذهب إلى أبعد من ذلك وتقدم عروض لإعادة العرض المتخصص UICollectionViewDropProposal ، وهو subclass
فئة من UIDropProposal ويسمح لك بتحديد ، بالإضافة إلى عملية التشغيل ، معلمة intent إضافية للمجموعة Collection View
.معلمةتخبر intent المجموعةCollection View
ما إذا كنا نريد وضع العنصر "المهمَل" داخل خلية موجودة ، أو ما إذا كنا نريد إضافة خلية جديدة . هل ترى الفرق؟ في حالة المجموعة ،Collection View
يجب أن ننوي نيتنا .في حالتنا ، نريد دائمًا إضافة خلية جديدة ، لذلك سترى ما ستكون معلمة intent الخاصة بنا متساوية.نختار المُنشئ الثاني لـ UICollectionViewDropProposal :
في حالتنا ، نريد دائمًا إضافة خلية جديدةوستأخذمعلمة intent القيمة .insertAtDestinationIndexPath بدلاً من ذلك.insertIntoDestinationIndexPath .
لقد استخدمت مرة أخرى الثابت المحسوب هو نفسه ، وإذا كانتإعادة تنظيم ذاتي ، فأنا أقوم بنقل .move ، وإلا فإنني أقوم بنسخ .copy . في كلتا الحالتين ، نستخدم .insertAtDestinationIndexPath ، أي إدخال خلايا جديدة.حتى الآن لم أقم بتنفيذ أسلوب PerformDrop ، ولكن دعونا نلقي نظرة على ما يمكن أن تفعله مجموعة بالفعلCollection View
بهذه القطعة الصغيرة من المعلومات التي قدمناها لها.اسحب الصورة منSafari
محرك البحثGoogle
، وتظهر علامة "+" خضراء أعلى هذه الصورة ، تشير إلى أن معرض الصور الخاص بنا ليس جاهزًا لقبول هذه الصورة ونسخها معها URL
فحسب ، بل يوفر أيضًا مكانًا داخل المجموعة Collection View
:
يمكنني النقر فوق زوج آخر من الصور في Safari
، و ستكون هناك بالفعل 3 صور "تم سحبها":
ولكن إذا رفعت إصبعي و "أسقطت" Drop
هذه الصور ، فلن يتم وضعها في معرض الصور الخاص بنا ، ولكن ببساطة العودة إلى أماكنها السابقة ، لأننا لم نقم بعد بتنفيذ أسلوب PerformDrop .
يمكنك أن ترى أن المجموعة Collection View
تعرف بالفعل ما أريد القيام به.المجموعة Collection View
هي شيء رائع للغاية بالنسبة للآلية.Drag & Drop
، لديها وظائف قوية للغاية لهذا الغرض. بالكاد لمسناها بكتابة 4 أسطر من التعليمات البرمجية ، وقد انتقلت بالفعل إلى حد بعيد في تصور "إعادة الضبط" Drop
.دعنا نعود إلى الكود ونطبق طريقة PerformDrop .
في هذه الطريقة ، لن نتمكن من الحصول على 4 أسطر من التعليمات البرمجية ، لأن طريقة PerformDrop أكثر تعقيدًا قليلاً ، ولكن ليس كثيرًا.عندما "إعادة تعيين" Drop
، ثم طريقة performDrop نحن بحاجة لتحديث نموذجنا، وهو معرض للصور imageGallery صورة السرد الصور ، ونحن بحاجة إلى تحديث لدينا مجموعة البصرية CollectionView .لدينا سيناريوهان مختلفان "لإعادة التعيين" Drop
.إذا كان هناك "إعادة تعيين" Drop
من مجموعة collectionView الخاصة بي ، فيجب علي "إعادة تعيين" Drop
عنصر المجموعة في مكان جديد وإزالته من المكان القديم ، لأنني في هذه الحالة أنقل ( .move ) عنصر المجموعة هذا. هذه مهمة تافهة.هناك "إعادة تعيين" Drop
من تطبيق آخر ، ثم يجب علينا استخدام خاصية itemProvider لعنصر العنصر " المسحوب " لتحديد البيانات.عندما نقوم بإجراء "إعادة تعيين" Drop
في مجموعة collectionView ، تزودنا المجموعة بمنسق منسق. أولا وقبل كل شيء، نحن أفاد منسق منسق ، فإنه destinationIndexPath ، أي indexPath "-destination"، "إعادة تعيين" Drop
، وهذا هو المكان الذي سيكون "إعادة تعيين".
ولكن قد يكون الوجهة الوجهة indexPath صفريًا ، حيث يمكنك سحب الصورة "المهملة" إلى ذلك الجزء من المجموعة Collection View
الذي ليس المكان بين بعض الخلايا الموجودة ، لذلك يمكن أن تكون صفرية . إذا حدث هذا الموقف ، فأنا أقوم بإنشاء مسار مسار باستخدام عنصر العنصر 0 في قسم القسم 0 .
يمكنني اختيار أي indexPath آخر ، لكنني سأستخدم هذا indexPath بشكل افتراضي.الآن نعرف أين سنقوم بإجراء "إعادة التعيين" Drop
. علينا أن نمر عبر جميع عناصر المنسق "القابلة لإعادة التوطين" المقدمة من المنسق . يحتوي كل عنصر من هذه القائمة على نوع UICollectionViewDropItem ويمكنه تزويدنا بمعلومات مثيرة جدًا للاهتمام.على سبيل المثال، اذا كان يمكنني الحصول sourceIndexPath من item.sourceIndexPath ، أنا أعرف بالضبط ما هو عليه "السحب والإسقاط" Drag
يتم في حد ذاته، الذات، ومصدر السحب Drag
هو عنصر المجموعة مع indexPath يساوي sourceIndexPath :
لست مضطرًا حتى إلى إلقاء نظرة على localContext في هذه الحالة لمعرفة أن هذا "السحب والإفلات" تم داخل مجموعة collectionView . واو!
الآن أعرف المصدر sourceIndexPath ووجهة " الوجهة " الوجهة indindPathDrag & Drop
، وتصبح المهمة تافهة. كل حاجة I القيام به هو تحديث نموذج بحيث المصدر و"نقطة المقصد" يتم عكسها، ومن ثم تحديث جمع CollectionView ، والتي سوف تحتاج إلى إزالة عنصر من مجموعة sourceIndexPath وإضافته إلى جمع destinationIndexPath .حالتنا المحلية هي الأبسط ، لأنه في هذه الحالة Drag & Drop
لا تعمل الآلية في نفس التطبيق فقط ، ولكن في نفس المجموعة collectionView ، ويمكنني الحصول على جميع المعلومات اللازمة باستخدام منسق المنسق. دعنا ننفذها في هذه الحالة المحلية الأبسط:
في حالتنا ، لا أحتاج حتى LocalObject ، الذي قمت "بإخفائه" في وقت سابق عندما قمت بإنشاء dragItem والذي يمكنني الآن استعارته من العنصر " المسحوب " في مجموعة العناصر في نموذج item.localObject . سنحتاج إليها عند "إغراق" Drop
الصور في "سلة المهملات" ، الموجودة في نفس التطبيق ، ولكنها ليست نفس مجموعة collectionView . الان انا بحاجة اثنين فقط IndexPathes : مصدر sourceIndexPath و "نقطة المقصد" destinationIndexPath .أحصل على المعلومات أولاًimageInfo عن الصورة في المكان القديم من النموذج وإزالتها من هناك. ثم تضاف إلى مجموعة من الصور من بلدي نماذج imageGallery المعلومات imageInfo صورة مع مؤشر جديد destinationIndexPath.item . هذه هي الطريقة التي قمت بها بتحديث النموذج الخاص بي:
الآن يجب أن أقوم بتحديث مجموعة collectionView نفسها. من المهم أن نفهم أن كنت لا تريد أن تفرط كافة البيانات في مجموعتي collectionView باستخدام reloadData () في وسط "السحب والإسقاط"Drag
، لأنه يعيد "العالم" كله من معرضنا من الصور، التي هي سيئة جدا، وأنا لا. بدلاً من ذلك ، سأقوم بتنظيم العناصر وإدراجهاالعناصر بشكل فردي:
قمت بحذف عنصر مجموعة collectionView مع sourceIndexPath وأدرجت عنصر مجموعة جديدًا مع destinationIndexPath .يبدو أن هذا الرمز يعمل بشكل رائع ، ولكن في الواقع ، يمكن لهذا الرمز "تعطل" تطبيقك. والسبب هو أنك تقوم بإجراء العديد من التغييرات على مجموعة collectionView الخاصة بك، وفي هذه الحالة يجب أن تتم مزامنة كل خطوة من خطوات تغيير المجموعة مع النموذج بشكل طبيعي ، وهو ما لا تتم ملاحظته في حالتنا ، حيث نقوم بإجراء العمليتين في نفس الوقت: الحذف والإدراج. ومن هنا جمع collectionViewسيكون في مرحلة ما في حالة غير متزامنة مع النموذج.ولكن هناك طريقة رائعة حقًا للتغلب على هذا ، وهي أن مجموعة collectionView لديها طريقة تسمى PerformBatchUpdates لها إغلاق ( closure
) وداخل هذا الإغلاق يمكنني وضع أي عدد من هذه العناصر ، و insertItems ، و moveItems ، وكل ما أريده:
الآن سيتم حذف deleteItems و insertItems كعملية واحدة ، ولن يكون هناك نقص في مزامنة النموذج الخاص بك مع collection collectionView .وأخيرًا ، آخر ما نحتاج إلى فعله هو أن تطلب من المنسق تنفيذ "إعادة التعيين" وتحريكها Drop
:
بمجرد رفع إصبعك من الشاشة ، تتحرك الصورة ، كل شيء يحدث في نفس الوقت: "إعادة التعيين" ، تختفي الصورة في مكان ومظهر في مكان آخر.دعونا نحاول نقل صورة الاختبار "البندقية" في معرض الصور الخاص بنا إلى نهاية السطر الأول ...
... "وإعادة تعيينها":
كما أردنا ، فقد تم وضعها في نهاية السطر الأول.مرحى!
كل شيء يعمل!الآن لن نتعامل مع الحالة المحلية ، أي عندما يأتي عنصر "إعادة التعيين" خارج ، أي من تطبيق آخر.للقيام بذلك ، نكتب آخر في الكود فيما يتعلق sourceIndexPath . إذا لم يكن لدينا sourceIndexPath ، فهذا يعني أن عنصر "إعادة التعيين" جاء من الخارج وسيتعين علينا استخدام نقل البيانات باستخدام itemProver لعنصر resettable item.dragItem.itemProvider :
إذا قمت "بالسحب والإفلات" Drag
خارج و "إسقاط" "Drop
ثم هل تصبح هذه المعلومات متاحة على الفور؟ لا ، أنت تحدد البيانات من الشيء "الذي تم سحبه" بشكل غير صحيح. ولكن ماذا لو كانت العينة تستغرق 10 ثوانٍ؟ ماذا ستفعل المجموعة في هذا الوقت ollection View
؟ بالإضافة إلى ذلك ، قد لا تصل البيانات بالترتيب الذي طلبناه به. لإدارة هذا ليس بالأمر السهل ، Apple
واقترح ollection View
لهذه الحالة تقنية جديدة تمامًا لاستخدام البدائل Placeholders
.أنت تضع Collection View
عنصرًا نائبًا في مجموعتك Placeholder
، Collection View
وتدير المجموعة كل شيء نيابة عنك ، لذلك كل ما عليك فعله عند تحديد البيانات أخيرًا هو أن تطلب من العنصر النائب Placeholder
الاتصال بالعنصر النائب سياق السياق الخاص بهوأخبره أنك تلقيت المعلومات. بعد ذلك ، قم بتحديث العنصر النائب الخاص بالنموذج والسياق ، حيث يقوم تلقائيًا بتبديل الخلية بالعنصر النائب Placeholder
بإحدى الخلايا الخاصة بك ، وهو ما يتوافق مع نوع البيانات التي تلقيتها.نقوم بتنفيذ كل هذه الإجراءات من خلال إنشاء سياق عنصر نائب PlaceContext يدير العنصر النائب Placeholder
والذي تحصل عليه من منسق المنسق ، ويطلب منك "إعادة تعيين" عنصرDrop
العنصر إلى العنصر النائب . سأستخدم المُهيئ لسياق العنصر النائب للعنصر النائبPlaceholder
الذي "يلقي" dragItem على UICollectionViewDropPlaceholder :
الكائن الذي أنا ذاهب إلى "رمي" Drop
أنه item.dragItem ، حيث البند - عنصر لل دورة، ونحن يمكن أن "رمي" Drop
مجموعة من الأشياء coordinator.items . نرميها واحدة تلو الأخرى. إذن item.dragItem هو ما " نسحبهDrag
" Drop
. الوسيطة التالية لهذه الوظيفة هي العنصر النائب ، وسأقوم بإنشائها باستخدام مُهيئ UICollectionViewDropPlaceholder :
للقيام بذلك ، أحتاج إلى معرفة أين سأقوم بإدراج العنصر النائبPlaceholder
، مثل insertionIndexPath ، بالإضافة إلى معرف خلية reuseIdentifier .من الواضح أن الوسيطة insertionIndexPath تساوي الوجهة الوجهة indindPath ، وهي IndexPath لوضع الكائن "الذي تم سحبه" ، ويتم حسابه في بداية أسلوب PerformDropWith .الآن دعونا نلقي نظرة على معرف خلية reuseIdentifier . يجب عليك تحديد نوع خلية الخلية التي يحددها محدد موقعك Placeholder
. ليس لدى المنسق المنسق خلية " معبأة مسبقًا" لتحديد الموقعPlaceholder
. أنت من يجب أن يقرر خلية الخلية هذه . لذلك ، مطلوب معرف معرف reuseIdentifiercell الخلية المعاد استخدامها الخاص بك storyboard
بحيث يمكن استخدامه كنوع PROTOTYPE.سوف أسميها "DropPlaceholderCell" ، ولكن في الأساس يمكنني تسميتها أيا كان.هذه مجرد سلسلة السلسلة التي سأستخدمها في الأعمال المتعلقة بي storyboard
لإنشاء هذا الشيء.عد إلى بلدنا storyboard
وقم بإنشاء خلية خلية للعنصر النائب Placeholder
. للقيام بذلك ، نحتاج فقط إلى تحديد مجموعة Collection View
وفحصها. في المجال الأول ، Items
أتغير 1
إلى2
. يؤدي هذا على الفور إلى إنشاء خلية ثانية لنا ، وهي نسخة طبق الأصل من الخلية الأولى.
نختار خليتنا الجديدة ImageCell
، ونعين المعرف " DropPlaceholderCell
" ، ونحذف جميع UI
العناصر من هناك ، بما في ذلك Image View
، نظرًا لأن هذا PROTOTYPE يستخدم عندما لا تصل الصورة بعد. نضيف مؤشر نشاط جديد من لوحة الكائن هناك Activity Indicator
، وسوف يتم تدويره ، مما يتيح للمستخدمين معرفة أنني أتوقع بعض بيانات "إعادة التعيين". أيضا تغيير لون الخلفية Background
لفهم أنه عندما "إعادة تعيين" من الصورة خارج يعمل بالضبط هذه الخلية خلية كنماذج أولية:
بالإضافة إلى نوع من الخلايا الجديدة لا ينبغي أن يكون ImageCollectionVewCell، لأنه لن يكون هناك صور فيه. سأجعل هذه الخلية خلية UIollectionCiewCell TYPE عادية ، لأننا لا نحتاج إلى أي Outlets
عنصر تحكم:
دعنا نقوم بتكوين مؤشر النشاط Activity Indicator
بحيث يبدأ في الحركة من البداية ، ولن أضطر إلى كتابة أي شيء في الرمز لبدء تشغيله. للقيام بذلك ، انقر فوق الخيار Animating
:
وهذا كل شيء. لذا ، قمنا بعمل جميع الإعدادات لهذه الخلية DropPlaceholderCell
، ونعود إلى الكود الخاص بنا. الآن لدينا محدد رائع Placeholder
جاهز للذهاب .كل ما علينا فعله هو الحصول على البيانات ، وعندما يتم استلام البيانات ، فإننا فقط نخبر العنصر النائب عن هذا السياق ، وسوف نتبادل العنصر النائبPlaceholder
والخلية "الأصلية" الخاصة بنا مع البيانات ، وسنقوم بإجراء تغييرات على النموذج.انا ذاهب الى "تحميل" كائن واحد ، والذي سيكون البند الخاص بي باستخدام طريقة loadObject (ofClass: UIImage.self) (المفرد). يمكنني استخدام رمز item.dragItem.itemProvider المورد itemProvider ، والتي سوف توفر عناصر البيانات لديها البند بشكل غير متزامن. من الواضح أنه إذا كان iitemProvider متصلاً ، فإننا نحصل على كائن "إعادة تعيين" iitem خارج هذا التطبيق. فيما يلي طريقة loadObject (ofClass: UIImage.self) (المفرد):
لم يتم تنفيذ هذا الإغلاق الخاص فيmain queue
. ولسوء الحظ ، كان علينا التبديل إلى main queue
استخدام DispatchQueue.main.async {} من أجل "التقاط" نسبة العرض إلى الارتفاع للصورة في متغير الجوانب المحلي .نحن حقا أدخلت اثنين من المتغيرات المحلية عنوان الصورة و aspectRatio ...
... وسوف "الصيد" لهم تحميل الصور صورة وURL رابط لا :
إذا كان اثنان من المتغيرات المحلية عنوان الصورة و aspectRatio لا يساوي صفرا ، وسوف نطلب سياق نائب placeholderSontext باستخدام طريقة commitInsertionامنحنا الفرصة لتغيير نموذج معرض الصور الخاص بنا :
في هذا التعبير ، لدينا insertionIndexPath - هذا هو indexPath لإدخاله ، وقمنا بتغيير نموذج معرض الصور الخاص بنا . هذا كل ما عليك القيام به، وهذه الطريقة سوف تحل تلقائيا نائبا Placeholder
على الخلية خلية من الطبيعي استدعاء أسلوب cellForItemAt .لاحظ أن insertionIndexPath يمكن أن يكون مختلفًا تمامًا عن destinationIndexPath . لماذا؟
نظرًا لأن أخذ عينات البيانات قد يستغرق 10 ثوان ، بالطبع ، فمن غير المحتمل ، ولكن قد يستغرق 10 ثوان. خلال هذا الوقت ، Collection View
يمكن أن يحدث الكثير في المجموعة . يمكن إضافة خلايا جديدة الخلايا ، كل شيء يحدث بما فيه الكفاية سريعة.استخدم دائمًا insertionIndexPath ، و insertionIndexPath فقط ، لتحديث نموذجك.كيف نقوم بتحديث نموذجنا؟نحن تضاف الى مجموعة imageGallery.images هيكل imagemodel ، ويتألف من نسبة الارتفاع aspectRatio وصورة URL عنوان الصورة ، الذي قدم لنا دعم المقابلة من قبل مزود .يعمل هذا على تحديث نموذج imageGallery الخاص بنا ، وتفي لنا طريقة الالتزام بالاعتماد . لم تعد بحاجة إلى القيام بأي شيء إضافي ، ولا إدراج ، ولا حذف ، ولا شيء من هذا. وبالطبع ، بما أننا في إغلاق ، نحتاج إلى إضافة الذات. .
إذا أردنا لسبب غير قادرين على الحصول على نسبة الارتفاع aspectRatio و URL
الصورة عنوان الصورة من المقابلة قبل مزود ، قد وردت خطأ خطأ بدلا من مزود ، لدينا ليعرفوا سياق placeholderContext ، تحتاج إلى تدمير هذا عنصر نائب Placeholder
، لأننا كل نفس، وأننا لا نستطيع الحصول على بيانات أخرى:
هناك شيء واحد يجب أخذه في الاعتبار هو URLs
أنها تأتي من أماكن مثل هذه Google
؛ في الواقع ، يحتاجون إلى تحويلات طفيفة للحصول على "نظيف"URL
للصورة. يمكن رؤية كيفية حل هذه المشكلة في هذا التطبيق التجريبي في ملف Utilities.swift
على Github .لذلك ، عند تلقي URL
صورة ، نستخدم خاصية imageURL من فئة عنوان URL :
وهذا كل ما عليك فعله لقبول شيء ما خارج المجموعة داخل المجموعة Collection View
.دعونا نراها في العمل. تشغيل تعدد المهام في وقت واحد لدينا التطبيق التجريبي ImageGallery
و Safari
محرك البحث Google
. في Google
البحث عن صور لموضوع "الفجر" (شروق الشمس). في Safari
بني بالفعلDrag & Drop
الآلية ، حتى نتمكن من تحديد إحدى هذه الصور ، والاحتفاظ بها لفترة طويلة ، ونقلها قليلاً وسحبها إلى معرض الصور الخاص بنا.
يشير وجود علامة الجمع الخضراء "+" إلى أن تطبيقنا جاهز لقبول صورة طرف ثالث ونسخها إلى مجموعتك في الموقع المحدد من قبل المستخدم. بعد "إعادة تعيين" الصورة ، يستغرق الأمر بعض الوقت لتنزيل الصورة ، وهي تعمل في الوقت الحالي Placeholder
:
بعد اكتمال التنزيل ، يتم وضع صورة "إعادة التعيين" في المكان الصحيح ، Placeholder
وتختفي:
يمكننا الاستمرار في "إعادة تعيين" الصور والمكان في مجموعات المزيد من الصور:
بعد عمل "إعادة الضبط" Placeholder
:
نتيجة لذلك ، يمتلئ معرض الصور لدينا بصور جديدة:
الآن أنه من الواضح أننا قادرون على التقاط الصور من الخارج، ونحن لسنا بحاجة لاختبار صورة، وسنقوم بإزالة:
لدينا viewDidLoad يصبح في غاية البساطة: فهو نقوم به لدينا Controller
Drag
و Drop
تفويض وإضافة التعرف على لفتة قرصة ، الذي ينظم عدد من الصور في كل سطر:
بالطبع ، يمكننا إضافة ذاكرة تخزين مؤقت لصور imageCache :
سنقوم بتعبئة imageCache عند "إعادة التعيين " Drop
في أسلوب PerformDrop ...
وعند الجلب من "الشبكة" في فئة ImageCollectionViewCell المخصصة : وسنستخدم
ذاكرة التخزين المؤقت imageCache عند تشغيل خليةخلية معرض الصور الخاص بنا في الفئة المخصصة ImageCollectionViewCell :
الآن نبدأ بمجموعة فارغة ...
... ثم "إسقاط" صورة جديدة في مجموعتنا ...
... يتم تحميل الصورةPlaceholder
وتعمل ...
... وتظهر الصورة في المكان الصحيح:
نواصل ملء مجموعتنا خارج:
يأتي تحميل الصورPlaceholders
والعمل ...
وتظهر الصور في المكان الصحيح:
لذا ، يمكننا أن نفعل الكثير مع معرض الصور الخاص بنا: املأه خارجًا ، وأعد تنظيم العناصر بالداخل ، وشارك الصور مع التطبيقات الأخرى نيامي.نحتاج فقط إلى تعليمها كيفية التخلص من الصور غير الضرورية من خلال "إعادة تعيينها"Drop
في "سلة المهملات" المقدمة على شريط التنقل على اليمين. كما هو موضح في قسم "ميزات التطبيق التجريبي لمعرض الصور " ، يتم تمثيل "سلة المهملات" بفئة GabageView ، التي ترث من UIView ويجب أن نعلمها قبول الصور من مجموعتنا ollection View
.إعادة تعيين Drop
صور المعرض إلى سلة المهملات.
مباشرة من المكان - إلى المحجر. وأود أن أضيف إلى GabageView "التفاعل" التفاعل ، وسوف UIDropInteraction ، لأنني أحاول أن "إعادة تعيين" Drop
بعض الأشياء. كل ما نحتاجه لتقديم UIDropInteraction هو مفوض مفوض ، وسأعين أنفسنا ذاتيًا لهذا المفوض المفوض :
بطبيعة الحال ، يجب أن يؤكد فصل GabageView أننا ننفذ بروتوكول UIDropInteractionDelegate :
كل ما علينا القيام به لتشغيلهDrop
، هذا هو تنفيذ أساليب canHandle المعروفة لنا بالفعل ،sessionDidUpdate وأداء Drop .
ومع ذلك ، على عكس الطرق المماثلة للمجموعةCollection View
، ليس لدينا أي معلومات إضافية في شكل indexPath لمكان الإغراق.دعونا نطبق هذه الأساليب.داخل طريقة canHandle سيتم معالجتها فقط "السحب والإسقاط"Drag
، والتي تمثل صورة لUIImage . لذلك ، سأعيد true فقط إذا كانت session.canLoadObjects (ofClass: UIImage.self) :
في طريقة canHandle ،يمكنك فقط أن تقول بشكل أساسي أنه إذا كان الكائن "القابل للسحب" ليس صورة UIImage، ثم لا معنى لمواصلة إسقاط "إعادة" واستدعاء الأساليب اللاحقة.إذا كان الكائن "القابل للسحب" هو صورة UIImage ، فسنقوم بتنفيذ طريقة sessionDidUpdate . كل ما علينا القيام به في هذه الطريقة هو إرجاع عرض "إعادة تعيين" UIDropProposal الخاص بنا Drop
. وأنا على استعداد لقبول الكائن المحلي "المسحوب" فقط لـ UIImage TYPE للصورة ، والذي يمكن "إسقاطه" في Drop
أي مكان داخل GarbageView . بلدي GarbageView لن تتفاعل مع الصور، ملقاة خارج. لذلك أقوم بالتحليل باستخدام متغير session.localDragSessionما إذا كان هناك "إعادة تعيين" محلي Drop
، وأعيد جملة "إعادة تعيين" في شكل مُنشئ UIDropProposal مع وسيطة العملية التي تأخذ القيمة .copy ، لأن ALWAYS LOCAL "السحب والإفلات" Drag
في طلبي سيأتي من المجموعة Collection View
. إذا كان هناك "السحب والإسقاط" Drag
و "إعادة تعيين" Drop
من الخارج، ثم أعود لعرض "إعادة تعيين" في شكل مصمم UIDropProposal حجة العملية ، وتلقي قيمة .fobbiden ، وهذا هو "ممنوع" ونحن بدلا من اللون الأخضر علامة زائد "+" الحصول على علامة حظر "إعادة تعيين" .
نسخ صورة UIImage، سنقوم بمحاكاة انخفاض في مقياسه إلى ما يقرب من 0 ، وعندما تحدث "إعادة الضبط" ، سنقوم بإزالة هذه الصورة من المجموعة Collection View
.من أجل خلق الوهم بالصور "الإغراق والاختفاء" في "سلة المهملات" للمستخدم ، نستخدم طريقة معاينةForDropping ، جديدة علينا ، والتي تسمح لنا بإعادة توجيه "الإغراق" Drop
إلى مكان آخر وفي نفس الوقت تحويل الكائن "الملغى" أثناء الرسوم المتحركة:
في هذه الطريقة ، باستخدام مُهيئ UIDragPreviewTarget ، نحصل على عرض مسبق جديد للكائن الهدف ليتم إسقاطه وإعادة توجيهه باستخدام طريقة retargetedPreviewإلى مكان جديد ، إلى "علبة القمامة" ، مع انخفاض حجمها إلى الصفر تقريبًا:
إذا رفع المستخدم إصبعه لأعلى ، عندها تحدث "إعادة الضبط" Drop
، وأنا (مثل GarbageView ) أتلقى رسالة PerformDrop . في رسالة PerformDrop ، نقوم بإجراء "إعادة الضبط" الفعلية Drop
. بصراحة ، الصورة التي تم إلقاءها على GarbageView نفسها لم تعد تهمنا ، لأننا سنجعلها غير مرئية عمليا ، على الأرجح أن حقيقة الانتهاء من "إعادة الضبط" Drop
ستشير إلى أننا نزيل هذه الصورة من المجموعة Collection View
. ولتحقيق ذلك، نحن بحاجة إلى معرفة kollektsiiyu جدا جمع و indexPathالصورة المهملة فيه. من أين نأتي بهم؟لأن عملية Drag & Drop
تجري في تطبيق واحد، وهي متوفرة لنا جميعا المحلي: المحلي Drag
جلسة localDragSession لدينا Drop
جلسة الدورة ، والسياق المحلي localContext ، وليس لدينا مجموعة من sollectionView وكائن محلي localObject صورة إعادة تعيين، والذي يمكن أن نفعله في حد ذاته هو صورة من "معرض" أو indexPath . وبسبب هذا فإننا يمكن أن تحصل في طريقة performDrop الطبقة GarbageView جمع جمع ، واستخدامهمصدر بيانات كيف ImageGalleryCollectionViewController ونموذج imageGallery لديناController
، يمكننا الحصول على مجموعة من الصور من الصور نوع [ImageModel]:
مع مساعدة من المحليةDrag
جلسة localDragSession لديناDrop
جلسة جلسة تمكنا من الحصول على جميع "السحب" على GarbageView Drag
عناصر من البنود ، ويمكن أن يكون هناك الكثير، كما نعلم، و كانت الصور لدينا kolllektsii CollectionView . خلقDrag
عناصر dragItems مجموعتناCollection View
، قدمنا لكل "السحب"Drag
عنصرdragItem الكائن المحلية localObject ، الذي هو صورة من الصورة ، ولكننا نحن لا تأتي في متناول اليدين أثناء جمع إعادة التنظيم الداخلي CollectionView ، ولكن "إعادة تعيين" معرض الصور "سلة المهملات" نحن بحاجة ماسة في منشأة المحلية localObject "السحب" وجوه dragItem ، بعد كل هذا الوقت ليس لدينا منسق يشارك بسخاء المعلومات حول ما يحدث في مجموعة collectionView . لذلك، نحن نريد كائن محلي localObject وكان مؤشر indexPath في صورة مجموعة صور من نماذجناimageGallery . إجراء التغييرات اللازمة في أسلوب dragItems (في indexPath: IndexPath) الطبقة ImageGalleryCollectionViewController :
الآن يمكن أن نتخذها كل "pretaskivaemogo" عنصر البند هو localObject ، وهو مؤشر indexPath في صورة مجموعة صور من نماذجنا imagegallery ، وإرسالها إلى المؤشرات مجموعة فهارس و مجموعة indexPahes صور الحذف:
معرفة مؤشر مجموعة فهارس ومجموعة indexPahes صور الحذف في طريقة performBatchUpdatesجمع جمع علينا إزالة جميع الصور المحذوفة من نماذج الصور ومن جمع جمع :
تشغيل التطبيق، وملء مع معرض صور جديدة:
اختيار زوج من الصور التي نريد إزالته من معرض لدينا ...
... "رمي" لهم على أيقونة مع "صندوق للقمامة" ...
أنها خفضت تقريبا إلى 0 ...
... وتختفي من المجموعة Collection View
، مختبئين في "سلة المهملات":
حفظ الصور بين البدايات.
لحفظ معرض الصور بين عمليات التشغيل ، سنستخدم UserDefaults ، بعد تحويل نموذجنا إلى JSON
تنسيق. للقيام بذلك، ونحن سوف تضيف لدينا Controller
متغير defailts فار ...
... وفي نموذج ImageGallery و ImageModel بروتوكول قابل للترميز :
سلسلة سلسلة ، صفائف صفيف ، وURL ، و نقرا بالفعل على تنفيذ بروتوكول قابل للترميز ، وبالتالي ليس لدينا أي شيء آخر القيام به للحصول على الترميز العمل وفك ترميز نماذج ImageGallery في JSON
التنسيق.كيف نحصل على JSON
نسخة ImageGallery ؟للقيام بذلك ، قم بإنشاء متغير محسوب var json ، والذي يُرجع نتيجة محاولة تحويل نفسه ، ذاتيًا ، باستخدام JSONEncoder.encode () إلى JSON
التنسيق:
وهذا كل شيء. سيتم إرجاع البيانات إما نتيجة تحويل الذات إلى تنسيق JSON
، أو لا شيء إذا فشل هذا التحويل ، على الرغم من أن هذا التحويل لا يحدث أبدًا ، لأن هذا النوع قابل للتشفير بنسبة 100٪ . تستخدم اختياري متغير سلمان فقط لأسباب التماثل.الآن لدينا طريقة لتحويل نماذج ImageGallery إلى تنسيق البياناتJSON
. هل يحتوي متغير json على بيانات TYPE ؟ والتي يمكن تذكرها في UserDefaults .تخيل الآن أننا بطريقة ما تمكنا من الحصول على JSON
بيانات json ، وأود أن أعيد إنشاء نموذجنا منها ، وهو مثال لبنية ImageGallery . للقيام بذلك ، من السهل جدًا كتابة INITIALIZER لـ ImageGallery ، الذي تكون وسيطته إدخال JSON
بيانات json . سيكون هذا المُهيئ مُهيئ "السقوط" (failable
) إذا فشلت في التهيئة ، فإنها تتعطل وتعيد صفرًا :
أحصل على قيمة newValue باستخدام وحدة فك ترميز JSONDecoder ، أحاول فك تشفير بيانات json التي تم تمريرها إلى المُهيئ ، ثم تعيينها ذاتيًا .إذا تمكنت من القيام بذلك ، فعندئذ أحصل على نسخة جديدة من ImageGallery ، ولكن إذا فشلت محاولتي ، فعندئذٍ أعود صفر ، حيث فشلت التهيئة الخاصة بي.يجب أن أقول أنه لدينا هنا أسباب أكثر بكثير لـ "الفشل" ( fail
) ، لأنه من الممكن أن تكون JSON
بيانات jsonيمكن أن يفسد أو فارغًا ، كل هذا يمكن أن يؤدي إلى "سقوط" ( fail
) المُهيئ.الآن يمكننا تنفيذ READ JSON
البيانات ونموذج استرداد imagegallery طريقة viewWillAppear لدينا Controller
...
... وكذلك إدخال في المراقب didSet {} خصائص imagegallery :
ودعونا تشغيل التطبيق وملء لدينا معرض للصور:
إذا كنا إغلاق التطبيق وفتحه مرة أخرى، يمكننا أن نرى معرضنا السابق تم حفظ الصور في UserDefaults .الخلاصة
توضح هذه المقالة مدى سهولة دمج التكنولوجيا Drag & Drop
في iOS
تطبيق باستخدام مثال التطبيق التجريبي البسيط "معرض الصور" . هذا جعل من الممكن تحرير معرض الصور بالكامل ، و "رمي" الصور الجديدة من التطبيقات الأخرى هناك ، ونقل التطبيقات الموجودة وحذف التطبيقات غير الضرورية. وكذلك لتوزيع الصور المتراكمة في المعرض على تطبيقات أخرى.بالطبع ، نود إنشاء العديد من هذه المجموعات الخلابة المواضيعية للصور وحفظها مباشرة على iPad أو iCloud Drive. يمكن القيام بذلك إذا تم تفسير كل معرض من هذا القبيل على أنه UIDocument المخزنة بشكل دائم. سيسمح لنا هذا التفسير بالارتقاء إلى المستوى التالي من التجريد وإنشاء تطبيق يعمل مع المستندات. في مثل هذا التطبيق ، سيتم عرض المستندات الخاصة بك بواسطة مكون DocumentBrowserViewController ، وهو مشابه جدًا للتطبيق Files
. سيسمح لك بإنشاء صور UIDocument من نوع "معرض الصور" على كل من الخاص بك iPad
وعلى iCloud Drive
، وكذلك تحديد المستند المطلوب للعرض والتحرير.لكن هذا هو موضوع المقال التالي.PS كود التطبيق التجريبي قبل تنفيذ الآلية Drag & Drop
وبعدها على جيثب .