قمنا بتنفيذ سكرولر أفقي مع إيكولكتيونفيو عن طريق تخصيص أبعاد تخطيط تدفق جمع. أردنا إظهار مؤشر ترقيم الصفحات الأيسر ومؤشر ترقيم الصفحات الصحيح لنقول أن هناك المزيد من الخلايا إلى اليمين الأيمن الأيمن. لتحقيق ذلك، قمنا بكتابة قطعة التعليمات البرمجية أدناه في الطريقة ولكن لا يعمل بشكل صحيح. الحد الأقصى لعدد الخلايا التي يمكن عرضها هو 5 (أي حجم الصفحة هو 5). هذا الحل يعمل بشكل صحيح عندما كنا التمرير ببطء، ولكن لا يعمل عندما كنا التمرير سريع جدا. يرجى اقتراح كيف يمكننا تحقيق ذلك. طلب مارس 19 14 في 07: 16Bryan هانسن إيكولكتيونفيو تخطيط مخصص البرنامج التعليمي لمشروع حديث كنت بحاجة إلى ريفاكتور وجهة نظر أولية داخل التطبيق لدينا باد، لذلك قررت أن تعلم من الداخل والخارجية من إيكولكتيونفيو (قدم في دائرة الرقابة الداخلية 6)، واعتقدت ذلك من شأنه أن يجعل لبرنامج تعليمي جيد للمشاركة. مشروعي انتهى الأمر بحاجة إلى إيكولكتيونفيولايوت مخصص مما يعني أنني بحاجة للتعامل مع أكثر من منطق تخطيط مما لو كان معرف يستخدم إيكولكتيونفيوفولايوت أن أبل يوفر. ومع ذلك، إنشاء تخطيط مخصص يسمح أكثر بكثير مرونة التصميم، لذلك أن ما هو سوء أن يأخذك من خلال هنا. حسنا تذهب من خلال عملية خطوة بخطوة، وتغطي المفاهيم الرئيسية لتصميم تخطيط مخصص: في نهاية جيدا لديها تعمل بشكل كامل كومة الصورة تخطيط كما رأينا هنا. حتى إذا كنت ترغب في تنفيذ تخطيط الذي يختلف كثيرا عن تلك المقدمة هنا، فإن المفاهيم الأساسية تكون مشابهة تماما. حتى ركلة العودة مع المشروبات المفضلة لديك وفي المساء أو اثنين يول لديهم فهم كامل للتخطيطات المخصصة. المشروع النهائي متاح على جيثب. ويركز هذا البرنامج التعليمي على اي فون، ولكن إذا كنت ترغب في إنشاء مشروع لباد، فإن معظم ما قدم يجب أن تعمل مع الحد الأدنى من التغييرات. مرة واحدة يتم إنشاء مشروعك سيكون لديك عارية العظام دائرة الرقابة الداخلية التطبيق مع الأسهم إيفيوكونترولر. في هذا التصميم، سوف تناسب جميع محتويات وحدة تحكم وجهة نظرنا ضمن عرض المجموعة المقدمة تلقائيا. إذا كان التصميم الخاص بك يحتوي على عناصر أخرى مثل رأس ثابت أو أقسام تذييل، ثم تود تريد استخدام إيفيوكونترولر مع إيكولكتيونفيو باعتبارها واحدة من مشاهدتها الفرعية. تغيير رأس وحدة تحكم العرض بحيث الفئات الفرعية إيكولكتيونفيوكونترولر بدلا من ذلك: قد ترغب في إعادة تسمية وحدة تحكم العرض إلى شيء أكثر ملاءمة. ويمكن القيام بذلك بسهولة عن طريق اختيار اسم الفئة في التعليمات البرمجية واختيار تحرير غ ريفاكتور غ إعادة تسمية. يجب أيضا تحديث زيب المشار إليها عند استدعاء إينتويثنيبنام: في مندوب التطبيق الخاص بك لتعكس تغيير الاسم. نحن بحاجة أيضا إلى تغيير زيب المرتبطة مع وحدة تحكم عرض هذا أنه يشير إلى إيكولكتيونفيو بدلا من الأسهم إيفيو لديها الآن. حدد زيب مراقبين عرض، ثم فتح جزء مفتشي الجانب الأيمن إذا لم يتم فتح بعد. البحث عن مجموعة عرض الكائن من الجزء السفلي من الجزء الأيسر واسحب جزء جديد في الجزء الفرعي الأيسر أسفل العرض الموجود بالفعل. الآن لدينا مثال عرض المجموعة، ولكن وجهة نظرنا القديمة لا تزال مرتبطة مع وحدة تحكم. يتيح إصلاح ذلك. حدد طريقة العرض القديمة في الجزء الفرعي الأيمن وحذفها. الآن سحب السحب من مالك الملفات إلى عرض المجموعة. عند التحرير، حدد طريقة العرض من القائمة لتعيين طريقة عرض المجموعة كطريقة عرض طريقة عرض وحدات التحكم طريقة العرض. الآن هو وقت كبير لتنظيم ملفات كسكودي لدينا قليلا، قبل أن نواصل إضافة المزيد من الملفات الطبقة. إنشاء مجموعة هكود جديدة اسمه عرض وحدة تحكم واسحب وحدة تحكم عرض و زيب المرتبطة بها في هذه المجموعة. منذ جيدا أن يكون خلق منطقتنا تخطيط مخصص تماما، ونحن بحاجة إلى إنشاء مجموعة عرض تخطيط الفئة الفرعية التي سوف التعامل مع منطق تخطيط كل من سوبفيوس . ضمن مجموعة وحدة تحكم عرض إنشاء فئة كائن-C جديدة تسمى ببهوتوالبوملايوت أي الفئات الفرعية إيكولكتيونفيولايوتنكستنك، وجعل أيضا تغييرات زوجين على وحدات تحكم عرض زيب لدعم فئة تخطيط مخصصة لدينا. حدد وحدة التحكم عرض زيب مرة أخرى، ثم حدد مثيل عرض المجموعة في الجزء الفرعي الأيسر. في جزء المفتش الصحيح، حدد علامة التبويب سمات ثم مرر لأسفل إلى قسم عرض المجموعة. في هذا القسم، غير التنسيق من التدفق إلى مخصص. ثم أدخل بهوتوالبوملايوت في مجال الطبقة كشفت. في نهاية المطاف تحتاج بشكل جيد للوصول إلى مثيل هذا التخطيط من وجهة نظرنا وحدة تحكم لذلك يجب علينا إنشاء منفذ الملكية في التعليمات البرمجية وربطه في زيب. في وجهة نظر تحكم فئة فرعية استيراد رأس بهفتوالبوملايوت ثم إضافة خاصية خاصة ك إيبوتليت: التبديل مرة أخرى إلى ملف زيب. ضمن الكائنات. توسيع عرض المجموعة ثم السيطرة على السحب من الملفات المالك لتخطيط ألبوم الصور. عند هذه النقطة إذا كنت لتشغيل التطبيق، حتى لو كنت فعلت كل شيء صحيح، يود لا يزال يحدق في الهاوية السوداء لا مطمئنة جدا. يعد تعيين لون الخلفية في طريقة عرض المجموعة وسيلة جيدة للتحقق من إعداد العرض بشكل صحيح. في وحدة تحكم وجهة نظرنا في فيوديدلواد تعيين لون الخلفية للعرض: الآن يمكنك اطلاق النار حتى التطبيق الخاص بك والتأكد من أن وجهة نظر جمع يظهر كما ينبغي. يجب أن تشاهد لون الخلفية الرمادي الداكن الذي حددناه، بدلا من اللون الأسود الافتراضي. بعد ذلك، ابدأ العمل على طرق العرض المحددة التي سيعرضها مجموعتنا. إيكولكتيونفيو و إيتاتيفيو هي في الواقع مشابهة جدا في بعض النواحي. كلاهما يستخدم نمط تصميم الوفد لتسهيل التصفح والتفاعل مع مجموعة من وجهات النظر (الخلايا)، فضلا عن ضمان الأداء هو مقبول من خلال توفير وسيلة لذاكرة التخزين المؤقت وإعادة استخدام هذه الآراء. ومع ذلك، في حين أن إيتاتيفيو يفرض بعض التفاصيل من تصميمها البصري والتخطيط، إيكولكتيونفيوس لا تفرض أي نوع من التصميم البصري أو تخطيط، مما يسمح أكثر من ذلك بكثير التخصيص فيما يتعلق بعرض محتواه. حسنا تخصيص منطق العرض لدينا قريبا بما فيه الكفاية، ولكن أولا إنشاء بعض وجهات النظر القابلة لإعادة الاستخدام لعرض مجموعتنا. هذا هو مماثل جدا لإنشاء خلايا مخصصة ل إيتابيفيو. إنشاء مجموعة هكود جديدة اسمها عرض. ضمن هذه المجموعة، إنشاء فئة الهدف-C اسمه بلبومفوتوسيل الفئة الفرعية أيكولكتيونفيوسيل. الآن يتيح إبقاء الأمور بسيطة وتعيين فقط لون الخلفية على هذه الخلية داخل إينتويثفريم: الآن أن لدينا خلية بسيطة جاهزة للاستخدام، كانوا في طريقهم للحصول على بعض أساسيات منطق تخطيط تنفيذها. بداية جيدة من خلال إعلان بعض الخصائص التي من شأنها أن تسمح التخصيص من مختلف جوانب التخطيط. قم بإضافة الخصائص التالية إلى رأس بهفوتوالبوملايوت: يتيح تعيين بعض الإعدادات الافتراضية لهذه القيم لجعلها أسهل على أي مستهلكين لتخطيطنا. بما أنه يمكن لشخص ما تحديد تخطيط طريقة العرض الخاصة بهم في ملف زيب (كما فعلنا) أو تهيئة ذلك مباشرة في التعليمات البرمجية، نحن بحاجة إلى تجاوز كل من إينيت و إنيتوثكودر: أساليب. أيضا، بدلا من تكرار رمز، يتيح إنشاء طريقة اسمه الإعداد التي سوف تحصل على استدعاء أي من هذه. إضافة هذه الطرق إلى تنفيذ بهفوتوالبوملايوت: الآن كانوا الحصول على اللحوم والبطاطس منطق منطقتنا التخطيط. نحن بحاجة إلى تجاوز عدد قليل من الأساليب التي سيتم حساب وإعادة معلومات التخطيط إلى طريقة العرض المجموعة. على الرغم من أن هناك أكثر من طريقة واحدة للقيام بذلك، وأوصى أبلز النهج للتخطيطات التي تتغير بشكل غير منتظم وعقد مئات من العناصر (بدلا من الآلاف) هو لحساب وتخزين كل المعلومات تخطيط مقدما ومن ثم الوصول إلى ذاكرة التخزين المؤقت عندما طلبات عرض المجموعة ذلك. إذا كنت ترغب في معرفة المزيد، تحقق من وثائق التفاح على إنشاء تخطيطات مخصصة. كذلك استخدام القاموس لتخزين كل من المعلومات تخطيط لدينا. سيكون الهيكل كما يلي: إنشاء قاموس فرعي لكل نوع من طرق العرض واستخدام مسار الفهرس لكل عرض محدد كمفتاح وسمات تخطيطه كقيمة مقترنة. ثم في قاموس المستوى الأعلى بشكل جيد استخدام نوع معين من الرأي كمفتاح لدينا والقاموس الفرعي نحن فقط خلق كقيمة. يسمح هذا بالوصول السريع إلى سمات التخطيط لنوع معين من المشاهدة في مسار فهرس محدد. يتيح إضافة مفتاح استخدام جيدا لخلايانا الآن. تحديد ثابت نسترينغ لبالبومفوتوسيل في الجزء العلوي من تنفيذ بهفوتوالبوملايوت. أدناه، إضافة قاموس المستوى الأعلى كخاصية خاصة. لدينا الآن كل ما نحتاج إليه لتنفيذ بريبارلايوت. يتيح القيام بذلك تجاوز بريبارلايوت في تنفيذ بهفوتوالبوملايوت: المنطق هنا هو في الواقع جميلة مباشرة إلى الأمام. أولا، نخلق بعض القواميس القابلة للتغيير. ثم لكل قسم في طريقة العرض المجموعة نحن حلقة من خلال كل من عناصرها وخلق إيكولكتيونفيولايوتاتريبوتس استنادا إلى مسار الفهرس الحالي. وضعنا الإطار لسمات المشاهدات هذه ثم أضف هذه السمات إلى القاموس الفرعي. وبمجرد أن يتم تحميلها من خلال جميع أقسامنا، نضع القاموس الفرعي على قاموس المستوى الأعلى. في النهاية، وضعنا خاصية القاموس الخاص بنا إلى القاموس المؤقت الذي أنشأناه هنا. كنت قد لاحظت أننا ندعو فريمفورالبومفوتواتيندسباث: الذي نحن مكتوبة حتى الآن. أعتقد أننا يجب أن نفعل ذلك. إضافة فريمفورالبومفوتواتيندسباث: إلى تنفيذ بهفوتوالبوملايوت: يتم حساب الإطار استنادا إلى مسار الفهرس مرت في. أولا نحدد الصف والعمود الصحيح لهذا العنصر. ثم نحن تحديد مجموع المبلغ الإجمالي التباعد الأفقي بين العناصر. ثم، إذا كان لدينا أكثر من عمود واحد، ونحن تقسيم مجموع تباعد للوصول إلى تباعد لكل بند. الآن يمكننا حساب تعويض الأفقي لدينا الإطار. لاحظ أننا نقوم بتعيين هذه القيمة بحيث يصبح إطارنا على وحدات بكسل كاملة، مما يضمن أنها تبدو حادة. أحد التفاصيل الدقيقة هنا هو أننا يجب أن الكلمة الكلمة في هذه المرحلة، وليس في السطر قبل حيث نحن مقسوما على عدد الأعمدة. وهذا يضمن أن المباعدة بين العناصر بشكل صحيح ونحن نذهب عبر أننا جولة القيمة بعد ضرب من العمود. التالي نحسب الإزاحة العمودي لدينا ثم إرجاع الإطار على أساس أصول وحجم البند. الآن بعد أن تم تحديد تخطيطنا، نحن بحاجة إلى تجاوز الطرق التي سوف تمرير هذا مرة أخرى إلى وجهة نظر المجموعة عند الحاجة إليها. أول تجاوز واحد هو لايوتاتريبوتسفوريليمنتينركت. تحتاج هذه الطريقة إلى إعادة كافة سمات التخطيط المطلوبة لمنطقة محتوى معينة من طريقة عرض المجموعة. منذ كل شيء كان يتعامل مع حتى الآن هي الخلايا، ونحن ببساطة بحاجة إلى معرفة الخلايا التي تقع داخل المستطيل مرت في. هذا هو لطيف وسهل الآن أن لدينا كل من سمات تخطيط لدينا على استعداد في قاموس لايوتينفو لدينا. تجاوز لايتاتاتريبسفوريليمنتينركت: في بفوتوالبوملايوت على النحو التالي: نبدأ من خلال إنشاء مصفوفة قابلة للتحويل حيث يمكننا تخزين كافة السمات التي تحتاج إلى إرجاع. القادم كانوا في طريقهم للاستفادة من لطيفة على أساس كتلة التعداد القاموس ل كروز من خلال قاموس لايوتينفو لدينا. وتكرر الكتلة الخارجية من خلال كل من القواميس الفرعية التي أضيفت (فقط الخلايا في الوقت الراهن)، ثم نحن يتكرر من خلال كل خلية في القاموس الفرعي. سغراكتينتيرسكتريكت يجعل من السهل معرفة ما إذا كانت الخلية كانت تبحث في يتقاطع مع المستقيم الذي تم تمريره في. إذا كان يفعل ذلك، ونحن إضافته إلى مجموعة جيدا أن يمر مرة أخرى. الطريقة التالية التي نحتاج إلى تنفيذها أسهل حتى بسبب آلية التخزين المؤقت التي اعتمدناها. تجاوز ليتاتريبوتسفوريتماتيندكسباث: في بهفتوالبوملايوت على النحو التالي: جميع كانوا يفعلون هنا هو البحث عن القاموس الفرعي للخلايا ومن ثم إعادة سمات تخطيط خلية في تمرير في مسار الفهرس. لا يمكن أن يكون أكثر بساطة الطريقة الأخيرة التي نحن بحاجة إلى تجاوز للحصول على تخطيطنا وتشغيلها يحتاج إلى إرجاع حجم المحتوى الكلي لجميع المحتوى لدينا. تجاوز كولكتيونفيوكونتنتزيز: في بهفتوالبوملايوت على النحو التالي: هذا بحساب وإرجاع الحجم الإجمالي المطلوب لإظهار كل شيء في وجهة نظر مجموعتنا. ويستند الارتفاع إلى إجمالي عدد الصفوف والعرض هو ببساطة عرض طريقة عرض المجموعة نفسها. مع أساسيات تخطيطنا كاملة، نحن الآن بحاجة إلى إعادة بعض المحتوى صالحة من أساليب مصدر البيانات في الفئة الفرعية تحكم وجهة نظرنا. قبل إعادة أي خلايا إلى طريقة عرض المجموعة، نحتاج أولا إلى تعيين وحدة تحكم العرض كمصدر بيانات ومفوض لعرض المجموعة. فتح زيب المرتبطة مع وجهة نظرنا تحكم وسحب السيطرة من وجهة نظر جمع لملفات المالك. انقر فوق منفذ بيانات مصدر ثم كرر مرة أخرى تعيين منفذ المفوض. في رأينا وحدات تحكم رأس تعلن أنها تنفذ بروتوكول ل إيكولكتيونفيوداتاسورس و UICollectionViewDelegate. Now كانوا ذاهبين لتسجيل فئة الخلايا لدينا مع وجهة نظر جمع. هذا يحكي عرض المجموعة ما ينبغي أن يطلب. وهذا يتطلب وجود معرف للخلية. التبديل إلى تنفيذ وحدة تحكم وجهة نظرنا واستيراد BHAlbumPhotoCell. h وتحديد سلسلة لمعرف الخلية: الآن في نهاية فيوديدلود على وحدة تحكم وجهة نظرنا، تسجيل فئة الخلية للمعرف عرفنا: التالي نحن بحاجة إلى تنفيذ ثلاثة أساليب مصدر البيانات والتي سوف تبدو مألوفة لأولئك الذين عملوا مع إيتابلفيو من قبل. تنفيذ عدد أوفسكتيونسينكولكتيونفيو: على وحدة تحكم وجهة نظرنا: الآن ببساطة إرجاع عدد ثابت، وكذلك الحصول على المحتوى الحقيقي العمل قريبا بما فيه الكفاية. بعد ذلك، تنفيذ رقم أوفيتمزينسكتيون: على وحدة تحكم وجهة نظرنا: ثم تنفيذ كولكتيونفيو: سيلفوريتماتيندسباث: على وجهة نظرنا تحكم: حسنا، في المرة الأخيرة لاطلاق النار حتى محاكي ومعرفة ما إذا كان لدينا تخطيط يعمل. إذا كنت فعلت الأمور الصحيحة، يجب أن تشاهد مجموعة من المربعات الرمادية في 2 الأعمدة التي يمكنك التمرير خلال عموديا. الآن كان الحصول على مكان ما أشياء حسنا تبحث كبيرة حتى الآن، ولكن هل حاولت تدوير الجهاز إلى أفقي هم ليس تماما ما كانوا يبحثون عن. بدلا من عرض 2 الأعمدة وحفنة من المساحة غير المستخدمة على اليمين، إيتد يكون من الأفضل أن تظهر 3 أعمدة عندما في المناظر الطبيعية. ليتس ميك ذيس أوفيرد ويلروتاتيتوينتيرفاسورينتاتيون: المدة: على وحدة تحكم وجهة نظرنا: أولا نتحقق مما إذا كان الجهاز في المناظر الطبيعية، وإذا كان الأمر كذلك، ونحن تعيين عدد الأعمدة إلى 3 ثم ضبط إيتمينزيتس قليلا لجعل الأمور متباعدة بالتساوي. إذا كانت في بورترايت ثم وضعنا الأعمدة إلى 2 واستخدام نفس القيم التي وضعناها بشكل افتراضي في تخطيطنا. يتيح إعطاء هذا دوامة، النار حتى التطبيق الخاص بك ما يعطي لماذا لم نحصل على تخطيط حددنا وعلاوة على ذلك، إذا حاولت التمرير، أشياء تعمل بشكل غريب. وتتمثل المشكلة في أنه تم تحديث سمات التنسيق لدينا لتعكس الحالة الجديدة لعرضنا. هذا يسلط الضوء على واحدة من المهام التي يجب علينا القيام بها كلما تم إجراء تغييرات التي تؤثر على تخطيط العناصر في وجهة نظر مجموعتنا. نحن بحاجة إلى إبطال التخطيط، مما سيؤدي إلى إعادة حساب جميع سمات التخطيط لدينا. يتم ذلك عن طريق استدعاء إنفالديلايوت على المثال إيكولكتيونفيولايوت ويجب أن يحدث كلما أي من الخصائص المخصصة التي أضفناها إلى تغيير تخطيط لدينا. للقيام بذلك بشكل صحيح يجب أن ننفذ صراحة واضعي هذه الخصائص وإبطال تخطيط هناك أيضا. تنفيذ واضعي لكل من الخصائص المخصصة على بهفتوالبوملايوت: كل كاتب يتحقق أولا إذا كانت القيمة التي يتم تعيين متطابقة مع ما هو بالفعل تعيين وإرجاع إذا كان هذا هو الحال. إذا كان مختلفا، فإنه يحدد متغير المثيل ثم يلغي التخطيط. تشغيل التطبيق الخاص بك الآن، يجب أن تتصرف كما هو متوقع عند تدوير الجهاز. مع تخطيطنا القادمة على طول لطيف، يتيح وضع في القليل من الجهد نحو الحصول على بعض المحتوى الحقيقي لعرض. هذه المربعات الرمادية هي لطيفة وكل شيء، ولكن ما نريد حقا أن نرى هي بعض الصور الهوى لتسهيل توفير الصور إلى وجهة نظرنا مجموعة كانوا في حاجة الى بعض الفئات لتمثيلها. وتعتبر هذه جزءا من النموذج، ضمن التنظيم المفاهيمي نموذج عرض المراقب المالي من التطبيق لدينا. منذ التركيز على هذا البرنامج التعليمي هو على جمع المشاهدات إم لن تنفق الكثير من الوقت على فئات النموذج. عادة كنت قد تستخدم شيئا مثل كوريداتا أو حل التخزين المستمر آخر للتعامل مع هذا الجانب من التطبيق الخاص بك. لهذا البرنامج التعليمي ببساطة تحميل فئات النموذج ومن ثم إضافتها إلى المشروع الخاص بك. إنشاء مجموعة باسم نموذج. ثم سحب الملفات ل بهفوتو و بهلبوم في المجموعة. تأكد من تحديد المربع لتطبيقك في إضافة إلى الأهداف، وإذا كنت تقوم بسحب الملفات من موقع خارج ملفات المشروع، فتأكد أيضا من نسخ العناصر إلى مجلد مجموعات الوجهة. هذه الفئات هي واضحة جدا. بهفوتو لديه عنوان ورل المصغرة وصورة للصورة. بلبوم له اسم ومجموعة من الصور، والقدرة على إضافة أو إزالة صورة. الآن يمكننا إنشاء بعض الألبومات وإضافة صورة إلى كل بحيث لدينا شيء أكثر إثارة للاهتمام قليلا للعرض. في الجزء العلوي من تنفيذ وحدة تحكم وجهة نظرنا، استيراد BHAlbum. h و BHPhoto. h. بعد ذلك، إنشاء خاصية مصفوفة قابلة للتبديل لعقد كافة الألبومات: ثم، ضمن طريقة فيوديدلود على وحدة تحكم عرض إضافة ما يلي بعد تعيين لون الخلفية في طريقة العرض "المجموعة": يتم تحميل هذه الصور من مستودع جيثب لهذا المشروع و هي بعض من الصور المفضلة لدي أن إيف اتخذت على مدى السنوات القليلة الماضية. نحتاج أيضا إلى تحديث طرق عرض بيانات طريقة عرض المجموعة على وحدة تحكم العرض بحيث يتم إرجاع القسم الصحيح وعناصر العناصر: حسنا، حصلنا على بعض ألبومات الصور مع صورتين لكل منهما جاهزة للاستخدام. الآن نحن بحاجة إلى تحديث خلية صورتنا بحيث يمكن عرضها. إضافة الخاصية أويماجيفيو كما للقراءة إلى رأس بهلبومفوتوسيل ثم إعادة تعريفها كما ريادوريت في التنفيذ: وهذا يتبع الاتفاقية التي تستخدمها أبل لمقابلات فرعية على إيتابيفيووسيل، مما يسمح للمستهلكين من هذه الفئة لتغيير خصائص العرض الصورة المقدمة، ولكن لا تبديل عرض الصورة نفسها. المقبل أيضا إعداد عرض الصورة وبعض الجماليات الأخرى على زنزانتنا. من أجل سحب بعض من نظرة المرجوة، كانوا في طريقهم لتغيير بعض الخصائص على الخلايا دعم طبقة لدينا. للقيام بذلك نحن بحاجة لاستيراد إطار كوارتزكور. إضافة استيراد في الجزء العلوي من تنفيذ بلبومفوتوسيل: ثم تحديث إينتويثفريم: على بلبومفوتوسيل لمباراة ما يلي: وهنا وضع بعض الخصائص المختلفة على الخلايا لدينا طبقة لإعطاء الظل قطرة لطيفة والحدود تؤثر. ونحن أيضا إنشاء صورة عرضنا، تعيين بعض الخصائص للعرض، ثم إضافته كجهاز عرض فرعية لمحتوى الخلاياعرض. آخر بت من العمل نحن بحاجة إلى القيام به على زنزانتنا هو تجاوز بريبارتيفوريوس وإعادة تعيين أي صورة التي قد تم تعيينها على إيماجيفيو. وهذا يضمن أن خلايانا تبدو جديدة حتى عندما يتم إعادة استخدامها من قبل كولكتيونفيو. إضافة بريباردفوريوس إلى بلبومفوتوسيل: نحن بحاجة إلى استدعاء السوبر عند تجاوز هذه الطريقة بحيث يتم إعداد الخلية بشكل صحيح قبل إعادة استخدامها. الحلو، والآن يمكننا تعيين صورة على الخلية الصورة لدينا. على وجهة نظرنا مجموعة تحكم التحديث كولكتيونفيو: سيلفوريتماتيندكسباث: لتتناسب مع ما يلي: هنا نحن الاستيلاء على الألبوم لهذا القسم ثم الصورة لهذا البند. مرة واحدة لدينا أن نتمكن من تعيين صورة الصور على الخلية. اطلاق النار حتى التطبيق الخاص بك ومعرفة ما تحصل عليه بعد الانتهاء من الصور تحميل، فإنها ينبغي أن تكون مرئية في عرض المجموعة. في حديثه عن التحميل، هل لاحظت كم من الوقت استغرق لرؤية تلك الصور ويف أساسا الأرض لدينا تطبيقات الاستجابة لوقف، كما جعل الآن مستخدمينا الانتظار لجميع الصور المرئية لتحميل قبل أن يتمكنوا من رؤية أو التفاعل مع أي شيء. هذا إينت ستعمل قطع عليه. يتيح ريفاكتور وحدة تحكم الرأي لدينا بحيث يتم تحميل الصور في الخلفية بدلا من على قائمة الانتظار الرئيسية. هناك عدد قليل من النهج المختلفة التي يمكن أن تتخذ هنا، ولكن إيف اختيار لاستخدام نوبيراتيونكيوو كتلة القائم، والذي يتيح لنا تعيين أولويات للعمليات التي نقوم بها. أولا نحن بحاجة إلى إضافة خاصية خاصة لقائمة انتظار التشغيل في الجزء العلوي من وحدة تحكم وجهة نظرنا. الآن يتيح تهيئة نسوبيراتيونكيو وتعيينه على ممتلكاتنا. منذ عملياتنا مستقلة تماما عن بعضها البعض، وأيضا تعيين الحد الأقصى لعدد العمليات المتزامنة التي يمكن أن تؤدي في 3 الأمر الذي سيجعل الأمور تحميل أسرع. إضافة هذه الأسطر إلى الجزء السفلي من فيوديدلود على وجهة نظرنا المراقب المالي. بعد ذلك، حيث كنا سابقا تحميل صورة الصورة مباشرة في كل مرة كنا تحميل خلية، وأيضا وضع الآن طابور التشغيل لاستخدامها بحيث يتم تحميلها في الخلفية. استبدال تنفيذ القديم من كولكتيونفيو: سيلفوريتماتيندكسباث: على وجهة نظرنا تحكم مع ما يلي: لا تكون تخويف من قبل كل التزامن، والعمليات، إيفاد و ماغنوت يحدث هنا جيدا، ربما قليلا تخويف، ولكن فقط لأنه يمكن أن يكون خادعا للمبرمجين المتقدمة جدا. كل ما يفعله هو إنشاء نبلوكوبيراتيون مع كتلة التعليمات البرمجية التي سيتم تنفيذها في وقت لاحق من قبل نسوبيراتيونكيو. لأنه كان يشير إلى الذات داخل الكتلة، ونحن بحاجة أيضا إلى إنشاء إشارة ضعيفة منه لاستخدامها. سيتيح هذا الإعداد كله واجهة المستخدم لدينا أن تظل متجاوبة لمستخدمينا. عند تنفيذ كتلة، فإنه أولا بتحميل صورة الصورة كما فعلنا سابقا. ثم عبر غراند سنترال إيفاد نحن إرسال غير متزامن بعض العمل مرة أخرى إلى طابور الرئيسي لضبط الصورة على الخلية الصورة لدينا. وهذا ضروري لأن أي شيء يلمس واجهة المستخدم يحتاج إلى تشغيل على قائمة الانتظار الرئيسية. لأسباب تتعلق بالأداء كانت فقط لضبط الصورة إذا كانت الخلية تعمل مع لا يزال في الرأي، وبهذه الطريقة إذا كان المستخدم هو التمرير بسرعة حقا، ونحن لن تبطئ لهم عن طريق وضع الصور أنها لن نرى حتى واحد من التفاصيل الأخيرة هو أنه لأن قد تنتهي الصورة تحميل لفترة من الوقت بعد المكالمة الأصلية ل سيلفوريتماتيندكسباث: نحن يجب أن نعتمد على فوتوسيل التي كانت ديكويد لتكون الخلية الفعلية لمسار الفهرس لدينا بعد الآن. قد يكون قد تم إعادة استخدامها بالفعل إذا استمر المستخدم التمرير. بدلا من ذلك، نحن إعادة الحصول على الخلية استنادا إلى مسار الفهرس ثم قم بتعيين الصورة على عرض صورة الخلايا. (ملاحظة جانبية، إذا كان التطبيق لدينا يسمح للمستخدمين بإضافة أو إزالة أو إعادة ترتيب الصور، ونحن حتى لن تكون قادرة على افتراض أن مسار الفهرس الأصلي لا يزال صالحا، وبدلا من ذلك نحن بحاجة أيضا إلى أن ننظر إلى ذلك مرة أخرى على أساس ألبوم معين و الصورة.) مع إنشاء نسبلوكوبيراتيون جيدا الآن إضافة العملية إلى قائمة الانتظار لدينا، والتي سوف التعامل مع تنفيذ كتلة بالنسبة لنا. يتيح إعطاء هذا المدى ونرى كيف ينفذ. يجب تحميل الصور بشكل ملحوظ أسرع الآن أن يتم تحميل 3 في نفس الوقت والتمرير يجب أن يكون حريري على نحو سلس. عمل جميل الآن أن لدينا تحميل صورة واحدة لكل ألبوم، يتيح حتى أنتي والحصول على كومة كاملة من الصور تحميل وتبحث حادة. من أجل القيام بذلك كانت تسير للاستفادة من بعض الخصائص الأخرى على إيكولكتيونفيولايوتاتريبوتس. بالإضافة إلى وضع الإطار، يمكننا أيضا تعيين قيم ل transfer3D، ألفا، زيندكس، مخبأة. إذا كانت تلك أرينت بما فيه الكفاية يمكنك إنشاء فئة فرعية وإضافة خصائص مخصصة كذلك، على الرغم من أنني لن تكون تغطي ذلك هنا. لتحقيق نظرة التراص كانت تسير جيدا أن تستخدم transfer3D، و زيندكس. من الناحية النظرية ما نريد أن يكون صور مكدسة من أعلى إلى أسفل في كل قسم مع كل صورة وجود دوران طفيف تطبيقها. هذا جنبا إلى جنب مع الظل طبقة كنا بالفعل إضافة إلى الخلية هو كل ما هو ضروري لجعلها تبدو لطيفة. أولا حسنا التعامل مع التناوب نريد التناوب على كل صورة لتبدو نوعا من العشوائية والطبيعية، كما لو كان شخص ما فقط أسقطتهم على الطاولة. حسنا توليد بعض التناوب العشوائي لتحقيق هذا، ولكن الصيد الوحيد هو أننا بحاجة إلى هذه التناوب على البقاء متسقة مرة واحدة ولدت لهم حتى نتمكن من إعادة تحميل الخلايا دون تغيير لهم بشكل غير متوقع. للقيام بذلك بشكل جيد توليد عدد محدد من التناوب عند إنشاء أول تخطيط لدينا ومن ثم عصا جيدا لاستخدامها بطريقة يمكن التنبؤ بها من خلال استخدام ثابت. قم بإعلان هذه الثوابت أدناه حيث تقوم باستيراد رؤوسك في الجزء العلوي من تنفيذ بهفوتوالبوملايوت: ثم قم بإنشاء خاصية مصفوفة خاصة في تنفيذ بهفوتوالبوملايوت كذلك: كان يجري تحميل هذه المصفوفة مع بعض عمليات تناوب CATransform3D، لكننا بحاجة لاستيراد إطار كوارتزكور في مشروعنا أولا لهذا العمل. حدد ملف المشروع الخاص بك، ثم حدد علامة التبويب إنشاء مراحل في أعلى العمود الأوسط. قم بتوسيع قسم لينك بيناري ويث ليبراريز واضغط على الزر في أسفل اليسار. العثور على كوارتزكوري. إطار العمل وإضافته إلى المشروع الخاص بك: التالي، إضافة ما يلي في نهاية طريقة الإعداد على بهفتوالبوملايوت: أولا نحن إنشاء مجموعة قابلة للتبديل المؤقت أن نضيف كائنات ل. ثم نحن تشغيل من خلال حلقة لدينا، وخلق التناوب في كل مرة. نخلق نسبة عشوائية بين -1.1 و 1.1 ثم استخدام ذلك لإنشاء CATransform3D أنب. لقد اخترقت قليلا وأضفت بعض المنطق لضمان أن نسبة التناوب التي نولدها عشوائيا هي أقل 0.6 مختلفة من تلك التي تم إنشاؤها مسبقا. وهذا يضمن أن الصور في كومة لا تملك سوء الحظ من كل يجري استدارة بنفس الطريقة. مرة واحدة لدينا تحويل لدينا، ونحن إضافته إلى مجموعة مؤقتة عن طريق لفه في نسفالو ثم شطف وتكرار. بعد إضافة كل 32 تناوب وضعنا الخاصية مجموعة خاصة. الآن نحن بحاجة فقط لوضعه للاستخدام. إيف اختار لتعيين تحويل على موقعنا إيكولكتيونفيولايوتايوتاتريبوتس وبالمثل لكيفية وضع إطارها، من خلال استخدام طريقة المساعد الخاص. إضافة الأسلوب التالي نحو الجزء السفلي من تنفيذ بهفوتوالبوملايوت: تم الاستفادة من ثابت خطوة أعلننا في وقت سابق لقفز بعض القيم دوران بين الأقسام. هذا يسمح لنا للحصول على الأموال لدينا قيمة من صفيف التناوب، من خلال تعويض في من دوران الأولى في مجموعة على أساس القسم والبند من مرت في مسار الفهرس. نحن ثم وزارة الدفاع هذا الإزاحة من قبل روتاتيونكونت لضمان البقاء داخل حدود المصفوفات. لأننا اخترنا بذكاء خطوة من 3 وعدد تناوب الكلي من 32 يول لاحظ أنه إذا كان لدينا في نهاية المطاف وجود الكثير من الأقسام، وكذلك استخدام فعال كل دوران في مجموعة كما تعويض الانطلاق، على النحو الأمثل تمويه حقيقة أننا نستخدم قيم دوران المتكررة . الآن نحن بحاجة فقط لتعيينها على سمات تخطيط لدينا. الآن في بريبارلايوت. مباشرة أدناه حيث وضعنا الإطار على موقعنا إيكولكتيونفيولايوتاتريبوتس يتيح تعيين transfer3D باستخدام لدينا ترانسفورمورالبومفوتواتيندكس: الأسلوب: يتيح إعطاء هذا دوامة ونرى ما نحصل عليه. حسنا، حصلنا على مجموعة متنوعة من التناوب التي يتم تطبيقها على الصور لدينا، ولكن كنت قد لاحظت أن حواف الإطار تبدو جاغي قليلا. هذا يمكن أن تكون ثابتة عن طريق طرح الخلايا لدينا طبقة لتنقيط. تحديث إينتيثفريم: الأسلوب على بلبومفوتوسيل بإضافة 2 خطوط جديدة: الآن يجب أن نرى بعض الصور استدارة بسلاسة في رأينا. هيك، والآن بعد أن حصلنا على صورنا الدورية لطيف، يتيح لكمة حتى الرقم في كل قسم لرؤية بعض الحلو، والعمل التراص الحلو في فيوديدلود على وجهة نظرنا تحكم، تحديث بداية الداخلية ل حلقة لزيادة عدد الصور في كل قسم: بعد زيادة عدد الصور، هل لاحظت أي فرق في وقت التحميل للصور صحيح، لدينا المزيد من الصور لتحميل، ولكن نحن نهتم أيضا أكثر عن الصورة العليا في كل كومة من تلك أدناه . كيف نحصل على هذا واحد لتحميل قبل الآخرين واحد من الأسباب اخترت نسوبيراتيونكيو كوسيلة لتحميل الصور بشكل غير متزامن لأنه يمكننا تحديد مستوى الأولوية لكل عملية نقوم بإنشائها. باستخدام هذا يمكننا فقط رفع أولوية تحميل الصورة العليا في كل قسم. في كولكتيونفيو: سيلفوريتماتيندكسباث: على وحدة تحكم وجهة نظرنا تعيين أولوية نسبلوكوبيرتيشن قبل إضافته إلى قائمة الانتظار: بالنسبة للبند العلوي وضعنا الأولوية إلى عالية، والبعض الآخر وضعنا إلى وضعها الطبيعي. يتيح تشغيل التطبيق مرة أخرى ومعرفة ما إذا كان هذا تحسين وقت التحميل لدينا. هم، فإنه لا يبدو أن يكون الكثير من الفرق. ولعل أولوية التحميل ليست القضية الوحيدة التي تواجه في الواقع، وتغيير أولويات المهام العملية لدينا هو فقط نصف المعركة. نحن بحاجة أيضا للتأكد من أن ما نعتبره أعلى صورة هو في الواقع وضعت في الجزء العلوي من وجهة نظرنا. للقيام بذلك نحن بحاجة إلى تحديد زيندكس من خلايانا بشكل صحيح. و يملي زيندكس ما إذا كانت خلية واحدة فوق أو تحت خلية أخرى. افتراضيا كل من الخلايا لدينا زيندكس من 0، مما يعني ترتيبها هو تعسفي تماما. وبما أننا قد يكون لدينا كمية متغيرة من الخلايا في كل قسم يتيح استخدام قيمة أساسية ل زيندكس لدينا ومن ثم زيادة على أساس مدى ارتفاع في كومة الخلية يجب أن تهبط، مع أعلى عنصر يجري أعلى. إضافة ثابت صحيح في الجزء العلوي من تنفيذ بهفوتوالبوملايوت: ثم في بريبارلايوت على بهفتوالبوملايوت تعيين زيندكس على سمات طبقة: الآن تشغيل التطبيق الخاص بك للتأكد من أن كانت العودة إلى تحميل الصور بسرعة. حسنا كان التقدم المحرز، فقط بضعة أشياء للعمل على قبل أن يكون لدينا تصميم ألبوم الصور المصممة بشكل كامل كل هذه الصور العظيمة بالتأكيد من السهل على العينين، ولكن هذا التصميم يدعو أيضا إلى اسم الألبوم ليتم عرضه أدناه كل كومة الصورة. لتسهيل إضافة هذا العنوان إلى طريقة عرض المجموعة، سيتم إدخال نوع عرض آخر في المزيج. يمكن أن تحتوي مشاهدات المجموعة على ما يصل إلى ثلاثة أنواع من الفئات الفرعية للعرض القابلة لإعادة الاستخدام في تخطيطها: الخلايا (التي استخدمناها في مداخن الصور)، وجهات النظر التكميلية، ومناظر الديكور. إذا كان العرض الخاص بك يتطلب أي نوع من البيانات من وحدة تحكم العرض للعرض، ثم تحتاج إلى استخدام إما خلايا أو وجهات النظر التكميلية. وجهات النظر الديكور من ناحية أخرى هي أساسا لأغراض جمالية، وينبغي عدم استخدام أي بيانات من وحدة تحكم العرض. نظرا لأن مشاهدات عنواننا ستعرض اسم كل ألبوم، فنحن بحاجة إلى استخدام طرق عرض إضافية لهذه الملفات. معظم العمل بشكل جيد أن تفعل ستكون مشابهة جدا للإعداد فعلنا لخلايانا. يتيح البدء من خلال تعديل تخطيطنا لخلق مساحة لعناوين الألبوم. للقيام بذلك، نحتاج إلى إضافة المزيد من المساحة العمودية بين الجزء السفلي من كومة واحدة وبدء واحد أسفله. بدلا من جعل الارتفاع ثابتة وغير مرنة، ويتيح إضافة خاصية تسمح للمستهلكين من فئة تخطيط لدينا لضبط الارتفاع أنفسهم. في رأس بهفتوالبوملايوت إضافة خاصية ل تيتالهيت: مرة أخرى، تحتاج أيضا إلى إبطال تخطيطنا إذا تغير هذا الخاصية، لذلك يسمح صراحة بتنفيذ واضعة لارتفاع العنوان: إضافة ما يلي أدناه أساليب الضبط الأخرى على بهفتوالبوملايوت: يتيح أيضا تعيين الافتراضي المعقول لهذه الخاصية ضمن طريقة الإعداد: كان أيضا بحاجة إلى سلسلة أخرى فريدة من نوعه لعرض العنوان على وشك إضافة. منذ وجهات النظر التكميلية والديكور تحتاج إلى سلسلة نوع للتسجيل يتيح جعل هذه السلسلة العامة حتى نتمكن من استخدامه للتسجيل وكذلك في قاموس التخطيط. تحديد نسترينغ في رأس رأس بهفوتوالبوملايوت ثم تعيينه في التنفيذ: تقسيم حتى تعريف وإعداد يضمن أن المستهلكين استخدام ثابت، وليس قيمة محددة. بعد ذلك نحتاج إلى إنشاء سمات تخطيط لكل عرض عنوان ضمن بريبارلايوت. وهذا يتطلب إضافة قاموس فرعي آخر حتى نتمكن من تخزين سمات تخطيط العناوين بنفس الطريقة التي فعلناها لخلايانا. At the beginning of prepareLayout on BHPhotoAlbumLayout create another dictionary:Now, since we only need one title per section, lets add it at the same time were adding attributes for the first cell, conveniently using the index path we already have on hand. Update the inner-loop on prepareLayout to match the following:Notice that we need to supply the kind when creating the layout attributes for Supplementary views. This can be used to differentiate Supplementary views used in your layout that happen to use the same class, but require different layout attributes. (Imagine if we also wanted to have a smaller subtitle below the album name telling users how many photos are in the album. We could use the same view class, but we might want to use a decreased height) For our view we only have one type of supplementary view. Finally, we need to set the titleLayoutInfo sub-dictionary on the top level dictionary: Add the following towards the end of prepareLayout. right after setting the cellLayoutInfo on newLayoutInfo:Since were once again calling a private helper method to obtain the frame for our title, we need to go implement that as well. After the other existing frame helper method add frameForAlbumTitleAtIndexPath:Calculating the frame for our title is a bit simpler since we can just base it off the frame of our cells. We do however need to update our cell frame and total content size calculations to take account for the added height of the titles. Update frameForAlbumPhotoAtIndexPath: to take account of the title height when calculating originY : Also, update the height calculation on collectionViewContentSize: to take account of the title height:The last thing we need to do is override the method which obtains layout attributes for a supplementary view at a specific index path. Add the following method on BHPhotoAlbumLayout below where we do this for our cells already:Thats all we need to update on our layout to handle album titles. Because of how weve structured our layoutInfo cache, no changes are necessary for layoutAttributesForElementsInRect: to correctly handle this additional view. If you run the app now, youll see that extra height has been added between each stack. Next we need to create a class for our album title views. Before we had subclassed UICollectionViewCell, but that class is used only for cells of a collection view. For supplementary views we need to subclass UICollectionReusableView. Create a class within the View grouping called BHAlbumTitleReusableView which subclasses UICollectionReusableView. This title view will be pretty basic, well just add one label, giving us a place to set our album name. Create a readonly property called titleLabel to the header of BHAlbumTitleReusableView and then re-define it as readwrite in its implementation:Now well setup our label on the view. Unlike cells where we need to add subviews to the contentView . here we add them directly on the view. Update initWithFrame: on BHAlbumTitleReusableView to the following:We also need to be sure to reset the labels text whenever the view is reused. Override perpareForReuse on BHAlbumTitleReusableView :Alright weve got some titles, before we can use them we need to define a reuse identifier and register them with the collection view. At the top of our view controller implementation define a static NSString for the identifier: Next, import the BHAlbumTitleReusableView header at the top of our view controller implementation. Then add the following towards the end of viewDidLoad :Now well implement another data source method and return instances of BHAlbumTitleReusableView when the collection view asks for them. Add collectionView:viewForSupplementaryElementOfKind:atIndexPath: to our view controller:Theres nothing special going on here, we get the album for this section and set its name on our title views label. Alright, now lets see how our app is looking BOOM Weve got title labels, this thing is really starting to come together. Now that everything is laid out nicely, lets make the view a little more visually interesting by swapping out the dull gray background color for a nice subtle textured look. A great website for finding textures which arent visually overbearing is subtlepatterns. They have dozens of textures in both normal and retina sizes. I found this concrete wall texture to work great. Create a new group named Images within the existing Supporting Files group. Download a texture you like and then add the image files to the Images group. We can easily add a background texture to our collection view by creating a UIColor with a pattern image. In viewDidLoad on our view controller subclass update the background color we set on the collection view to use a pattern:At this point I think weve got a pretty good looking view, but Id also like to walk through how to add decoration views. Although this may be bit contrived, what were going to do now is add a small emblem decoration tucked up above the photo stacks. Its something youll only see when pulling down on the view before it snaps back into place, but its a good way to show how to work these kinds of views in because they may be more beneficial in other layout designs. Now that weve added cells and supplementary views, this will feel somewhat familiar. The primary difference is that our collection views data source wont need to be involved with the logic of the emblem view. The layout and the emblem view itself will handle everything on their own. Create a class named BHEmblemView as a subclass of UICollectionReusableView within the View group. For this view, were just going to display the same image all the time and this can easily be done within initWithFrame: on our emblem view. If you wanted to create a more customizable decoration view, you would need to make use of custom properties, which requires creating a subclass of UICollectionViewLayoutAttributes and then setting those up within the layout. You can use the image I used or use your own. Were also going to create a simple class method to return the size of the image so that we can provide this to our layout class without needing to instantiate an emblem view. Add a class method named defaultSize to the BHEmblemView header: Switch to the implementation of BHEmblemView and add a constant for the image name: Then implement defaultSize :Now well set up the view to display our emblem. Update initWithFrame: on BHEmblemView to match the following:The collection view will handle instantiation of the emblem view using the frame which well set on its layout attributes. Unlike cells and supplementary views, decoration views are registered with the layout rather than the collection view. Import the BHEmblemView header into the BHPhotoAlbumLayout implementation. Define another kind string for our emblem at the top of the BHPhotoAlbumLayout : Register the emblem view at the end of the setup method on BHPhotoAlbumLayout :Unlike the other views weve added, only the layout class deals with decoration views, thus we perform the registration here rather than in the view controller. Now lets create another private frame helper method for our emblem. Add frameForEmblem towards the bottom of BHPhotoAlbumLayout :Here we get the default size for our emblem and then calculate an origin that will place the view centered horizontally and ending 30pts above the very top of the collection view and pass it back. Now that we have our frame, were ready to create some layout attributes for the emblem. Since were only going to have one emblem, we can create our attributes before we loop through all the sections and rows. Once again, well create a sub-dictionary and add it to the layoutInfo dictionary. Update prepareLayout to match the following:The last step is to return layout attributes for a decoration view with a given kind at a given index path. Add layoutAttributesForDecorationViewOfKind:atIndexPath: to BHPhotoAlbumLayout :Thats all we need to do for the emblem. If you fire up the app and pull down on the view, you should see the emblem tucked above. This covers the fundamental steps of creating custom layouts with UICollectionView. I hope its been enlightening From this point you could consider implementing custom animations for adding, deleting or moving items. Maybe it would be cool to tap a stack and have it expand out into a more detailed view for that album. If theres a lot of interest I may consider covering more advanced topics in an additional tutorial. Although I dont have a comments section here, feel free to email me any feedback regarding this tutorial at bryanehansengmail . copy 2012 Bryan Hansen
No comments:
Post a Comment