Name of the blog

Short description of the blog

5G چیست؟

 

 

 

اصطلاح  5Gبه نسل بعدی شبکه تلفن همراه دلالت می‌کند. از زمانی که شبکه4G گستره زیادی از نواحی را پوشش می‌داد، شاهد پیدایش شبکه‌های LTE، LTE پیشرفته و XLTE اوپراتور ورایزن بوده‌ایم که پهنای باند وسیعی را دارا هستند.

 

هسته شکل‌گیری تمام این پیشرفت‌ها یکی بوده و زیر چتر حمایت 5G ساخته ‌شده است. 5Gبه‌عنوان جانشین واقعی 4G شناخته‌ شده و در مجموعه‌ای به‌مراتب متفاوت‌تر از شبکه‌های امروزی عرضه خواهد شد.

 

دانستن تفاوت‌های بین این شبکه‌ها ضروری است، چراکه عملکرد گوشی شما بسیار به کانکشنی که انتخاب کرده‌اید، بستگی دارد و اهمیت آن فراتر از علامتی است که به‌عنوان نماد برقراری اتصال در بالای سمت راست صفحه‌نمایش گوشی شما ظاهر می‌شود.

 

مهاجرت از 3G به 4G باعث ایجاد یک جهش بزرگ در راستای بهبود سرعت دانلود شد. درصورتی‌که قبل از آن استفاده از خدمات آنلاین سرویس‌هایی مثل Netflix با 3G  تقریباً غیرممکن بود. این ‌یکی از مواردی است که لزوم مهاجرت به 4G را برای کاربران مطرح می‌کند.اما امتیاز فناوری جدید  5Gتنها محدود به‌سرعت فوق‌العاده‌اش نیست، این فناوری به حل یکی از بزرگ‌ترین مشکلات امروز ما یعنی اتصال به یک کانکشن مورد اعتماد و سریع،‌ خواهد پرداخت. مهم نیست که کاربر نزدیک به پنجره بوده و داخل یا خارج ساختمان باشد، حتی اینکه در یک زیرزمین هم دفن شده باشد اهمیتی ندارد! اولین هدف 5Gاین است که کاربر در همه شرایط به اینترنت دسترسی داشته باشد.

 

یکی از دلایلی که به چنین کانکشن قدرتمندی نیاز داریم، این است که ساپورت 5G تنها محدود به اسمارت‌فون‌ها نیست و قابلیت پشتیبانی از دیگر ابزارها نظیر ساعت‌های مچی هوشمند، گجت‌های تناسب‌اندام و ترموستات‌های هوشمند خانگی را دارد

 

fdgdfgdf

پیاده سازی یک کنترل اختصاصی (Custom control)

در این مبحث، شما یک کنترل جهت امتیاز دهی (rating control) برای برنامه ی FoodTracker پیاده سازی خواهید کرد. در پایان، برنامه ی شما ظاهری مشابه نمونه زیر خواهد داشت:

آنچه خواهید آموخت

·         ایجاد المان های UI اختصاصی به همراه فایل های source code مرتبط. متصل کردن المان های UI در storyboard به کد آن ها.

·         تعریف کلاس اختصاصی.

·         پیاده سازی یک متد initializer (مقداردهنده ی اولیه) داخل کلاس اختصاصی مورد نظر.

·         استفاده از کلاس UIView به عنوان یک ظرف.

·         نحوه ی به نمایش گذاشتن محتوای view ها برای کاربر با کدنویسی (programmatically).

تعریف یک View اختصاصی

برای اعطا کردن قابلیت امتیاز دهی به غذاهای موجود در برنامه به کاربر، لازم است یک control تعریف کنید که تعدادی ستاره نمایش داده و کاربر برای نظر دادن درباره ی غذا باید این ستاره ها را پر کند. روش های مختلفی برای پیاده سازی این قابلیت وجود دارد. در آموزش حاضر از این روش استفاده خواهید کرد: یک view اختصاصی با کد تعریف کرده و سپس آن را در storyboard خود مورد استفاده قرار خواهید داد.

در زیر نمایی از کنترلی که جهت نمایش و تخصیص امتیاز به غذا پیاده سازی خواهید کرد را مشاهده می کنید:

کنترل نام برده به کاربران این امکان را می دهد تا بر اساس میزان محبوبیت غذا، به آن ستاره تخصیص دهند. همان طور که می بینید این کنترل در کل 5 ستاره نمایش می دهد که پنجمین ستاره طبیعتا نشانگر بالاترین امتیاز برای آن غذا می باشد. زمانی که کاربر بر روی یک ستاره ضربه می زند، تمامی ستاره های منتهی به آن (از جمله خود ستاره ی انتخاب شده) از سمت چپ پر می شوند.

برای اقدام به طراحی ظاهر (UI)، قابلیت تعامل با کاربر و رفتار این کنترل، بایستی ابتدا یک کلاس فرزند view با پیاده سازی اختصاصی از UIView ایجاد کنید (یک custom view subclass از کلاس پایه UIView ایجاد نمایید.)

جهت ایجاد یک کلاس فرزند از UIView، مراحل زیر را گام به گام دنبال نمایید:

1.      این مسیر را طی کنید: File > New > File یا Command-N را فشار دهید.

2.      یک کادر محاوره ای نمایان می شود. در سمت چپ آن، گزینه ی IOS را انتخاب نمایید.

3.      گزینه ی Cocoa Touch Class را انتخاب نموده و بر روی Next کلیک کنید.

4.      داخل فیلد Class، عبارت RatingControl را وارد نمایید.

5.      حال گزینه ی UIView را از فیلد Subclass of انتخاب نمایید.

6.      زبان برنامه نویسی پروژه را بر روی Swift تنظیم نمایید.

7.      بر روی دکمه ی Next کلیک کنید.

محل ذخیره ی فایل به صورت پیش فرض بر روی دایرکتوری پروژه ی جاری شما تنظیم می شود.

گزینه ی Group به صورت پیش فرض بر روی اسم برنامه ی فعلی، FoodTracker تنظیم می شود.

در بخش Targets، می بینید که برنامه ی شما انتخاب شده اما تست های مربوط به آن انتخاب نشده اند.

8.      لازم نیست تنظیمات پیش فرض را دستکاری نمایید. کافی است بر روی Create کلیک کرده تا فایل مورد نظر ایجاد گردد.  

Xcode یک فایل جدید، حاوی RatingControl class: RatingControl.swift ایجاد می کند. RatingControl یک کلاس اختصاصی view، ارث بری شده از کلاس پایه ی UIView است (منظور از کلاس فرزند اختصاصی، کلاسی است که متدهای کلاس پایه در آن بازنویسی شده باشند).

9.      در فایل RatingControl.swift، تمامی comment هایی که همراه با قالب آماده (template) و پیاده سازی الگو (template implementation) ارائه می شوند را حذف نمایید.

در حال حاضر پیاده سازی کلاس بایستی مشابه زیر باشد:

import UIKit

class RatingControl: UIView {

}

View معمولا به دو روش زیر ایجاد می شود: روش اول عبارت است مقدار دهی اولیه ی یک view با یک فریم که به شما امکان می دهد view را به صورت دستی به UI اضافه کنید و روش دوم، عبارت است از واگذار کردن بارگذاری view به storyboard. برای هر یک از روش های نام برده یک متد initializer اختصاصی وجود دارد: به منظور مقداردهی اولیه و ایجاد یک فریم برای view از متد init(frame:) و برای روش دوم، محول کردن بارگذاری view به storyboard، از متد init?(coder:) استفاده می کنیم. یادآور می شویم که initializer متدی است که یک نمونه از روی کلاس جاری می سازد، property های آن کلاس را مقداردهی اولیه نموده و در صورت لزوم سایر تنظیمات آغازین را انجام می دهد.

در آموزش حاضر، از روش دوم اقدام به ایجاد view خواهید نمود (در storyboard با view خود کار خواهید کرد)، از این رو لازم است ابتدا (پیاده سازی) متد init?(coder:) کلاس والد view را override (بازنویسی) نمایید.

به منظور بازنویسی پیاده سازی متد initializer، مراحل زیر را گام به گام دنبال نمایید:

1.      داخل فایل RatingControl.swift، در زیر خط تعریف کلاس، این comment را درج نمایید:

1.       // MARK: Initialization

2.      حال در زیر این comment، ابتدا واژه ی init را تایپ کنید.

خواهید دید که ابزار پیشبینی و تکمیل کد (code completion) محیط کاری Xcode نمایان می شود.

3.      سومین متد را در لیستی که پدیدار می شود (init?(coder:)) انتخاب کرده و سپس کلید Return را فشار دهید.

init?(coder aDecoder: NSCoder) {

}

Xcode خود اسکلت (کد لازم) متد را برای شما درج می کند.

4.      یک پیغام خطا با آیکون قرمز رنگ به نمایش در می آید. بر روی Fix-it کلیک کرده تا Xcode کلیدواژه ی required را به متد initializer اضافه کند.

 

required init?(coder aDecoder: NSCoder) {

}

تمامی کلاس های ارث بری شده از UIView (کلاس های فرزند آن) که متد initializer را پیاده سازی می کنند، می بایست متد init?(coder:) را نیز در بدنه ی خود داشته و پیاده سازی کنند. کامپایلر زبان Swift به این امر واقف بوده و راه حل خود (جهت ویرایش کد) را در قالب fix-it برای شما ارائه می کند. Fix-it ها راه حل هایی هستند که کامپایلر در جواب خطاهای رخ داده به هنگام کدنویسی در اختیار شما قرار می دهد.

5.      کد زیر را جهت فراخوانی متد initializer کلاس والد اضافه نمایید.

super.init(coder: aDecoder)

 

هم اکنون بدنه ی متدinit?(coder:)  بایستی دربردارنده ی پیاده سازی زیر باشد:

required init?(coder aDecoder: NSCoder) {

super.init(coder: aDecoder)

}

به نمایش گذاشتن view اختصاصی

جهت نمایش view دلخواه، می بایست یک view به UI خود اضافه کرده، سپس ارتباطی بین view مورد نظر و کد متناظر آن برقرار نمایید.

جهت به نمایش گذاشتن محتوای view اختصاصی خود در رابط کاربری، مراحل زیر را به ترتیب دنبال نمایید:

1.      ابتدا فایل storyboard را باز نمایید.

2.      داخل storyboard، یک View object از کادر Object library انتخاب کرده، آن را بکشید و در سطح scene جاری جایگذاری نمایید، به طوری که این المان در قالب stackview و زیر آبجکت image view قرار گیرد. 

3.      پس از انتخاب view مورد نظر، کادر Size inspector را با کلیک بر روی آیکون ، در utility area باز نمایید. 

یادآور می شویم که Size inspector با کلیک بر روی دکمه ی پنجم از سمت چپ inspector selector bar باز می شود. در این کادر شما می توانید اندازه و مکان قرار گیری آبجکت مورد نظر را ویرایش نمایید.

4.      از منوی intrinsic size، گزینه ی Placeholder را انتخاب نمایید.  

5.      در زیر منو ی Intrinsic Size، داخل فیلد Height مقدار 44 و داخل فیلد Width مقدار عددی 240 را وارد کنید. اکنون کلید Return را فشار دهید.

در حال حاضر، UI برنامه می بایست ظاهری مشابه زیر داشته باشد.

6.      پس از انتخاب view مورد نظر، کادر  Identity inspector را باز نمایید.

یادآور می شویم که Identity inspector به شما این امکان را می دهد تا آن دسته از property ها و ویژگی های یک آبجکت که با identity آن مرتبط است، نظیر اینکه به کدام کلاس تعلق دارد را ویرایش نمایید. 

7.      داخل این کادر، فیلدی که Class نام دارد را یافته و آن را بر روی RatingControl تنظیم نمایید.

افزودن کنترل Button به View

تا اینجا، اصول اولیه ی یک کلاس فرزند از UIView با پیاده سازی اختصاصی خود (به نام RatingControl) را پایریزی کردید. حال در این بخش تعدادی دکمه به view دلخواه خود اضافه می کنید که کاربر با استفاده از آن ها می تواند به غذای مورد نظر امتیاز بدهد.

کار را با اضافه کردن یک دکمه ی ساده قرمز رنگ آغاز می کنیم که در view به نمایش در می آید.

جهت ایجاد یک دکمه در view اختصاصی خود، مراحل زیر را دنبال کنید:

1.      داخل بدنه ی متد init?(coder:)، دستورات زیر را جهت ایجاد یک دکمه ی قرمز رنگ وارد نمایید:

let button = UIButton(frame: CGRect(x: 0, y: 0, width: 44, height: 44))

button.backgroundColor = UIColor.redColor()

متد redColor()، رنگ کنترل دکمه را بر روی قرمز تنظیم می کند. در صورت تمایل می توانید سایر مقادیر پیش فرض UIColor نظیر blueColor() یا greenColor() را بکار ببرید.

2.      در زیر آخرین خط، کد زیر را وارد کنید:

1.       addSubview(button)

متد addSubview()، دکمه ای که تعریف کردید را به کلاس RatingControl اضافه می کند. 

پیاده سازی متد init?(coder:)  در بدنه بایستی به صورت زیر باشد:

required init?(coder aDecoder: NSCoder) {

super.init(coder: aDecoder)

 

let button = UIButton(frame: CGRect(x: 0, y: 0, width: 44, height: 44))

button.backgroundColor = UIColor.redColor()

addSubview(button)

}

برای اینکه به stack view اعلان کنید چگونه و با چه اندازه ای کنترل دکمه را نمایش دهد، لازم است intrinsic content size آن را مشخص نمایید. برای نیل به این هدف، کافی است پیاده سازی متد intrinsicContentSize را مانند زیر بازنویسی کرده تا پارامترهای تنظیم کننده ی اندازه ی دکمه با اندازه ای که قبلا در Interface Builder (برای کنترل مربوز) مشخص کردید، همخوانی داشته باشد:

override func intrinsicContentSize() -> CGSize {

return CGSize(width: 240, height: 44)

}

تست کنید: برنامه ی خود را اجرا کنید. برنامه هم اکنون باید یک مربع قرمز رنگ کوچک در view به نمایش بگذارد. این مربع قرمز رنگ همان کنترل دکمه است که در بدنه ی متد initializer اضافه کردید.

این دکمه و دکمه های دیگری که به برنامه اضافه می کنید، می بایست با کلیک کاربر، یک action را صدا زده و عملیات خاصی را انجام دهند. آن action تغییر امتیاز یک غذا است. 

به منظور اضافه کردن یک action به کنترل دکمه، مراحل زیر را به ترتیب دنبال نمایید:

1.      داخل فایل RatingControl.swift، قبل از آخرین (})، comment زیر را وارد کنید:

1.       // MARK: Button Action

در زیر comment، تابع زیر را وارد کنید:

func ratingButtonTapped(button: UIButton) {

print("Button pressed ")

}

در زمان حاضر، تابع print() را صرفا جهت کسب اطمینان از متصل بودن متد ratingButtonTapped(_:) به دکمه ی مورد نظر فراخوانی می کنیم. این تابع یک پیغام را در خروجی چاپ می کند. در اینجا منظور از خروجی همان ابزار console در پایین محیط کاری Xcode است که به منظور ثبت گزارش (درباره ی خطاها) و اشکال زدایی کد مورد استفاده قرار می گیرد.  

به زودی پیاده سازی این متد را که در حال حاضر صرفا پیغامی را در console چاپ می کند، با یک عملیات (پیاده سازی) واقعی جایگزین خواهید نمود. 

2.      متد init?(coder:) را پیدا کنید:

required init?(coder aDecoder: NSCoder) {

super.init(coder: aDecoder)

let button = UIButton(frame: CGRect(x: 0, y: 0, width: 44, height: 44))

button.backgroundColor = UIColor.redColor()

addSubview(button)

}

3.      قبل از متد addSubview(button)، این کد را درج نمایید:

2.       button.addTarget(self, action: #selector(RatingControl.ratingButtonTapped(_:)), forControlEvents: .TouchDown)

از قبل با الگوی target-action آشنایی دارید و از آن برای متصل کردن المان های رابط کاربری در storyboard به action method ها در کد استفاده کردید. در بالا همین کار را انجام می دهید، با این تفاوت که اتصال بین کد و المان UI در storyboard را با کدنویسی بر قرار می کنید. بدین صورت که شما متد ratingButtonTapped(_:) را به آبجکت button متصل می کنید و به مجرد اینکه کاربر بر روی دکمه کلیک می کند، رخداد .TouchDown اتفاق افتاده و به دنبال آن متد مذکور اجرا می شود. این رخداد، همان طور که از نامش پیدا است، به برنامه اعلان می کند که کاربر دکمه مورد نظر را فشار داده است.

از آنجایی که action را در سطح کلاس RatingControl تعریف کردید، به واسطه ی کلیدواژه ی self که به کلاس جاری اشاره دارد، همین کلاس را target و دریافت کننده ی پیغام رخداد فشرده شدن دکمه قرار می دهید.

عبارت #selector در خروجی مقدار Selector را برای متد ارائه شده برمی گرداند. Selector یک مقدار opaque است که با استفاده از آن متد خاصی را انتخاب می کنید و در واقع آن متد را به عنوان خروجی برمی گردانید (مقدار opaque مقداری است که جز نوع، هیچ اطلاعات دیگری درباره ی خود بروز نمی دهد).

 API های قدیمی معمولا از selector برای فراخوانی داینامیک متدها در زمان اجرا استفاده می کردند. اگرچه اغلب API های جدید block ها را جایگزین selector کرده اند، با این حال برخی از متدهای قدیمی همچون performSelector(_:) و addTarget(_:action:forControlEvents:) هنوز selector را (برای اشاره به متدی خاص) به عنوان آرگومان می گیرند.

در مثال جاری، عبارت #selector(RatingControl.ratingButtonTapped(_:)) مقدار selector را به عنوان خروجی برای متد #selector(RatingControl.ratingButtonTapped(_:)) برمی گرداند. با این کار به سیستم اجازه می دهید تا به محض فشرده شدن دکمه، متدی (action method) که شما تعریف نموده و به این دکمه متصل کردید را اجرا کند.

از آنجایی که برای ساخت کنترل دکمه از interface builder استفاده نکردید، برای تعریف action method نیز نیازی به IBAction ندارید. می توانید action را مانند یک متد معمولی تعریف کنید.   

پس از افزودن دستورات فوق، بدنه ی متد init?(coder:) می بایست به صورت زیر باشد:

required init?(coder aDecoder: NSCoder) {

super.init(coder: aDecoder)

let button = UIButton(frame: CGRect(x: 0, y: 0, width: 44, height: 44))

button.backgroundColor = UIColor.redColor()

button.addTarget(self, action: #selector(RatingControl.ratingButtonTapped(_:)), forControlEvents: .TouchDown)

addSubview(button)

}

تست کنید: برنامه ی خود را اجرا نمایید. به محض کلیک بر روی دکمه ی قرمز رنگ، بایستی پیغام Button pressed را در Console مشاهده نمایید.

اکنون زمان آن فرا رسیده تا اطلاعات لازم جهت امتیازدهی به غذا و نمایش آن امتیاز برای کاربر را در اختیار کلاس RatingControl قرار دهید. در وهله ی اول بایستی مقدار امتیاز –0،1،2،3،4 یا 5- را ذخیره کرده و رصد کنید. سپس دکمه هایی که کاربر با فشردن آن ها امتیاز غذا را مشخص می کند، ایجاد نمایید. می توانید مقدار امتیاز را با نوع int و مجموعه دکمه ها را به وسیله ی آرایه ای از آبجکت های UIButton تعریف نمایید.  

برای این منظور مراحل زیر را دنبال نمایید:

1.      داخل فایل RatingControl.swift، خط تعریف کلاس را پیدا کنید:

class RatingControl: UIView {

2.      در زیر این خط، کد زیر را وارد نمایید:

// MARK: Properties

var rating = 0

var ratingButtons = [UIButton]()

در حال حاضر تنها یک دکمه در view خود دارید، در حالی که برای نمایش میزان محبوبیت غذا و امتیاز آن در کل به 5 دکمه احتیاج دارید. جهت ایجاد این تعداد کنترل دکمه از یک حلقه ی for-in بهره می گیرد. این حلقه در یک دنباله، برای مثال مجموعه ای از اعداد صحیح، پیمایش کرده و یک مجموعه دستور را به دفعات مشخصی تکرار می کند. بجای یک دکمه، این ساختمان کد تعداد کل 5 دکمه را ایجاد می کند.

جهت ایجاد این تعداد کنترل دکمه، مراحل زیر را دنبال نمایید: 

1.      در فایل RatingControl.swift، متد init?(coder:) را پیدا کنید:

required init?(coder aDecoder: NSCoder) {

super.init(coder: aDecoder)

let button = UIButton(frame: CGRect(x: 0, y: 0, width: 44, height: 44))

button.backgroundColor = UIColor.redColor()

button.addTarget(self, action: #selector(RatingControl.ratingButtonTapped(_:)), forControlEvents: .TouchDown)

addSubview(button)

}

2.      حال آن را به صورت زیر ویرایش نمایید:

for _ in 0..<5 {

let button = UIButton(frame: CGRect(x: 0, y: 0, width: 44, height: 44))

button.backgroundColor = UIColor.redColor()

button.addTarget(self, action: #selector(RatingControl.ratingButtonTapped(_:)), forControlEvents: .TouchDown)

addSubview(button)

}

برای سازمان دهی کد و اطمینان از توگذاری صحیح تمامی خط های آن، می توانید کل ساختمان کد را انتخاب کرده و سپس Control-I را فشار دهید. 

لازم به ذکر است که عملگر (..<) خود عدد بزرگ را شامل نمی شود، بنابراین این بازه از 0 شروع شده تا 4 را پیمایش می کند که در کل به 5 بار اجرای دستورات منتهی می شود. در نتیجه ی بجای یک دکمه، با یک حلقه 5 دکمه یکجا ایجاد می کنید. در صورتی که نیازی به آگاهی از گام جاری اجرای (گام تکرار) حلقه نیست، می توانید از (_) استفاده کنید.   

3.      در بالای خط addSubview(button)، این دستور را اضافه کنید:

ratingButtons += [button]

هر کنترل دکمه ای که ایجاد می کنید، جهت ذخیره و رصد مقدارش، آن را به آرایه ی RatingsButtons اضافه می نمایید (تک تک دکمه هایی که با هر بار اجرای حلقه ایجاد می شود را داخل خانه های آرایه مزبور می ریزید).

هم اکنون بدنه ی متدinit?(coder:) می بایست مشابه نمونه ی زیر باشد:

required init?(coder aDecoder: NSCoder) {

super.init(coder: aDecoder)

for _ in 0..<5 {

let button = UIButton(frame: CGRect(x: 0, y: 0, width: 44, height: 44))

button.backgroundColor = UIColor.redColor()

button.addTarget(self, action: #selector(RatingControl.ratingButtonTapped(_:)), forControlEvents: .TouchDown)

ratingButtons += [button]

addSubview(button)

}

}

تست کنید: حال برنامه ی خود را اجرا نمایید. شاید این طور به نظر برسد که در UI برنامه تنها یک دکمه قابل مشاهده است. اما جالب است بدانید که حلقه ی for-in دکمه های ایجاد شده را مانند پشته بر روی هم قرار داده است. به منظور نمایش صحیح کنترل امتیاز دهی، شما باید چیدمان کنترل های دکمه در view را خود تنظیم نمایید.

عملیات لازم جهت تنظیم چیدمان و نمایش صحیح المان ها در UI، داخل بدنه ی متدی به نام layoutSubviews از کلاس UIView تعریف می شود. سیستم این متد را خود در زمان مناسب فراخوانی کرده و از این طریق به کلاس های ارث بری شده از UIView اجازه می دهد تا subview های مورد نظر را به نحو صحیح و با چیدمان دلخواه برنامه نویس در UI نمایش دهند.

برای تنظیم چیدمان کنترل های دکمه، لازم است پیاده سازی متد نام برده را بازنویسی (override) نمایید.

جهت بازنویسی این متد و نمایش دکمه ها در کنار هم، مراحل زیر را به ترتیب طی نمایید:

1.      داخل فایل RatingControl.swift، در زیر متد init?(coder:)، تابع زیر را وارد نمایید:

override func layoutSubviews() {

}

یادآور می شویم که با استفاده از قابلیت code completion محیط کاری Xcode می توانید (اسکلت) این متد را به صورت آماده در کد خود قرار دهید. 

2.      داخل بدنه ی این متد، کد زیر را تایپ کنید:

var buttonFrame = CGRect(x: 0, y: 0, width: 44, height: 44)

// Offset each button's origin by the length of the button plus spacing.

for (index, button) in ratingButtons.enumerate() {

buttonFrame.origin.x = CGFloat(index * (44 + 5))

button.frame = buttonFrame

}

این کد frame دکمه ها را ایجاد می کند، سپس با استفاده از حلقه ی for-in داخل آرایه ی دکمه ها پیمایش کرده و در هر بار اجرا مقادیر (مربوط به) frame هر یک از کنترل های مزبور را تنظیم می نماید.

متد enumerate() در خروجی یک collection برمی گرداند که داخل این collection تک تک المان های آرایه ی ratingButtons همراه با اندیس متناظر آن ها قرار داده شده است. این collection در واقع مجموعه ای از tuple ها (مقادیر گروهی مثال کلید-مقدار) است و در این مثال، هر tuple شامل یک دکمه + اندیس مربوطه ی آن می باشد. حلقه ی for-in که داخل بدنه ی تابع layoutSubviews مشاهده می کنید، مقدار دکمه و اندیس آن را به ترتیب داخل متغیرهای محلی button و index ذخیره می کند (مقدار آن ها را به متغیرهای محلی ذکر شده bind می کند). با استفاده از متغیر index، محل جدید فریم هر دکمه را محاسبه کرده و بعد آن را داخل متغیر button ذخیره می کند. مکان قرارگیری فریم ها برابر اندازه ی استاندارد دکمه با عرض و طول = 44 + padding = 5 (فاصله ی بین دکمه ها) ، ضرب در اندیس (شماره ی مکان قرار گیری) آن دکمه محاسبه و مقداردهی می شود.

در حال حاضر کد موجود در بدنه ی متد layoutSubviews() می بایست مشابه زیر باشد:

override func layoutSubviews() {

var buttonFrame = CGRect(x: 0, y: 0, width: 44, height: 44)

// Offset each button's origin by the length of the button plus spacing.

for (index, button) in ratingButtons.enumerate() {

buttonFrame.origin.x = CGFloat(index * (44 + 5))

button.frame = buttonFrame

}

}

تست کنید: برنامه ی خود را اجرا نمایید. خواهید دید که این بار دکمه ها در کنار هم قرار گرفته اند. یادآور می شویم که کلیک بر روی هر یک از دکمه ها، با توجه به کدی که تا به اینجا برای برنامه ی خود نوشته اید، صرفا سبب فراخوانی متد ratingButtonTapped(_:) و چاپ شدن پیغامی در console می شود.

می توانید console را برای داشتن فضای کاری بیشتر، پنهان نمایید. برای این منظور کافی است بر روی دکمه ی اشاره شده در تصویر زیر کلیک نمایید:

تنظیم میزان فاصله ی دکمه ها از هم و تعداد آن ها (در قالب دو متغیر/property مجزا)

اگر به خاطر داشته باشید، برای تنظیم تعداد کنترل های قابل مشاهده دکمه در UI و میزان فاصله ی بین آن ها، تاکنون در کد خود از عدد 5 استفاده کردید. به طور کلی نگه داشتن مقادیر hardcode شده به صورت پراکنده در متن برنامه روش مطلوب و بهینه ی کدنویسی تلقی نمی شود، چرا که در آن صورت، اگر بخواهید میزان فاصله ی بین دکمه ها را ویرایش کنید، می بایست هرجایی در کد که این مقدار به نشانی میزان فاصله ی بین المان ها بکار رفته را پیدا و متعاقبا ویرایش نمایید. حال این امر که مقدار مزبور نشانگر دو چیز کاملا متفاوت در کد شما است (یکی فاصله ی بین دکمه ها و دیگری تعداد آن ها) فقط به پیچیدگی هر چه بیشتر شرایط می افزاید.  

برای رفع این مشکل، دو متغیر تعریف می کنید که یکی محل نگهداری مقدار تعداد دکمه ها و دیگری ظرف نگهداری فاصله ی بین آن ها است. اکنون زمانی که می خواهید مقدار را تغییر دهید، فقط می بایست آن را در یک مکان واحد ویرایش کنید.

جهت تعریف دو متغیر (property) مجزا برای نگهداری فاصله ی بین دکمه ها و تعداد آن ها، مراحل زیر را دنبال نمایید:

1.      داخل فایل RatingControl.swift، بخش // MARK: Properties را پیدا کنید:

// MARK: Properties

var rating = 0

var ratingButtons = [UIButton]()

می توانید با استفاده از functions menu، به سرعت به بخش دلخواه در کد پیمایش کنید (بپرید). این منو با کلیک بر روی اسم فایل، در بالای editor area نمایان می شود.

2.      در زیر property های موجود، کد زیر را وارد نمایید:

let spacing = 5

این ثابت (constant) را جهت تعیین میزان فاصله ی بین دکمه ها در UI، تعریف می کنید.

3.      داخل بدنه ی متد layoutSubviews، مقدار نوشتاری (literal) که برای تعیین میزان فاصله ی بین دکمه ها استفاده می کردید را با ثابت spacing جایگزین نمایید:

buttonFrame.origin.x = CGFloat(index * (44 + spacing))

4.      در زیر ثابت spacing، ثابت دیگری به صورت زیر اضافه نمایید:

let starCount = 5

می توانید این ثابت را جهت تعیین تعداد دکمه هایی (ستاره ها) که کنترل برای کاربر به نمایش می گذارد، بکار ببرید.

5.      اکنون، داخل بدنه ی init?(coder:)، مقدار نوشتاری (literal) که به نشانه ی تعداد دکمه ها در متن برنامه استفاده کردید را با ثابت starCount جایگزین نمایید:

for _ in 0..<starCount {

تست کنید: اپلیکیشن خود را اجرا کنید. رفتار و ظاهر برنامه نباید نسبت به قبل تغییری کرده باشد.

تعریف یک ثابت جهت تعیین اندازه ی کنترل دکمه

اگر بخاطر داشته باشید، مقدار 44 را به نشانه ی اندازه ی کنترل دکمه، در کد خود بکار بردید. اما همان طور که قبلا گفته شد، مقادیر hardcode شده به صورت پراکنده در کد مطلوب نیست و بهتر است این روش کدنویسی را از متن برنامه ی خود حذف کنید. این بار دکمه ها را طوری تنظیم می کنید که خود را با اندازه ی view میزبان تطبیق دهند (container view ای که در ابتدای مبحث به storyboard خود اضافه کردید). این کار را با بازیابی مقدار طول (height) view میزبان و ذخیره ی آن داخل یک ثابت محلی انجام می دهید، سپس به راحتی و تنها یکبار از داخل هر متد به آن دست پیدا می کنید.    

جهت تعریف یک ثابت، برای ذخیره و نگهداری اندازه ی دکمه های کنترل امتیازدهی، مراحل زیر طی نمایید:

1.      داخل بدنه ی متد layoutSubviews()، این کد را قبل از اولین خط پیاده سازی اضافه نمایید:

// Set the button's width and height to a square the size of the frame's height.

let buttonSize = Int(frame.size.height)

با افزودن این کد layout را به طور قابل توجهی انعطاف پذیرتر می سازید.

2.      حال ثابت buttonSize را جایگزین مقدار 44 در بقیه ی کد نمایید:

var buttonFrame = CGRect(x: 0, y: 0, width: buttonSize, height: buttonSize)

// Offset each button's origin by the length of the button plus spacing.

for (index, button) in ratingButtons.enumerate() {

buttonFrame.origin.x = CGFloat(index * (buttonSize + 5))

button.frame = buttonFrame

}

برای اینکه stack view کنترل امتیاز دهی را به صورت دلخواه نمایش دهد، لازم است intrinsic content size (اندازه ی درونی) کنترل مورد نظر را بروز رسانی نمایید. این بار محاسبه ی اندازه ی هر یک از دکمه ها (ستاره ها) و فاصله ی بین آن ها را به متد intrinsicContentSize() واگذار می کنید.

برای این منظور کافی است کد زیر را بکار ببرید:

let buttonSize = Int(frame.size.height)

let width = (buttonSize * starCount) + (spacing * (starCount - 1))

return CGSize(width: width, height: buttonSize)

در متد init?(coder:)، اولین خط کد داخل ساختمان حلقه ی for-in را به صورت زیر ویرایش کنید:

let button = UIButton()

از آنجایی که پراپرتی frame دکمه ها را داخل متد layoutSubviews() تنظیم می کنید، دیگر در زمان ایجاد دکمه ها نیازی به مقدار دهی آن ها نیست.

کد موجود در بدنه ی متد layoutSubviews() اکنون می بایست مشابه نمونه ی زیر باشد:

override func layoutSubviews() {

// Set the button's width and height to a square the size of the frame's height.

let buttonSize = Int(frame.size.height)

var buttonFrame = CGRect(x: 0, y: 0, width: buttonSize, height: buttonSize)

// Offset each button's origin by the length of the button plus some spacing.

for (index, button) in ratingButtons.enumerate() {

buttonFrame.origin.x = CGFloat(index * (buttonSize + 5))

button.frame = buttonFrame

}

}

کد موجود در بدنه ی متد intrinsicContentSize می بایست مشابه نمونه ی زیر باشد:

override func intrinsicContentSize() -> CGSize {

let buttonSize = Int(frame.size.height)

let width = (buttonSize * starCount) + (spacing * (starCount - 1))

return CGSize(width: width, height: buttonSize)

}

بدنه ی متد init?(coder:) نیز بایستی مانند زیر باشد:

required init?(coder aDecoder: NSCoder) {

super.init(coder: aDecoder)

for _ in 0..<5 {

let button = UIButton()

button.backgroundColor = UIColor.redColor()

button.addTarget(self, action: #selector(RatingControl.ratingButtonTapped(_:)), forControlEvents: .TouchDown)

ratingButtons += [button]

addSubview(button)

}

}

تست کنید: برنامه را اجرا کنید. رفتار و ظاهر برنامه نسبت به قبل تغییر خاصی نکرده است. دکمه ها باید در کنار هم قرار گرفته باشند و کلیک بر روی هر کدام از دکمه ها هنوز بایستی سبب فراخوانی متد ratingButtonTapped(_:) شده و پیغامی را  در console ثبت کند. 

جایگزین کردن عکس ستاره بجای مربع های قرمز رنگ

در این بخش عکس یک ستاره ی پر شده و خالی را به هر یک از دکمه های کنترل اضافه می کنید:

 

می توانید تصاویر فوق را در پوشه ی Images/ از فایل پروژه ی دانلود شده انتخاب کنید یا در صورت تمایل تصاویر دلخواه خود را بکار ببرید. (کافی است مطمئن شوید اسم عکس های مورد استفاده ی شما با اسم عکس ها در کد کاملا همخوانی داشته باشد).  

به منظور افزودن تصویر به پروژه ی خود، مراحل زیر را طی نمایید:

1.      داخل project navigator، با کلیک بر روی Assets.xcassets، ابزار asset catalog را باز کنید.  

یادآور می شویم که asset catalog مکانی است که در آن محتوای برنامه ی خود نظیر عکس، متن و غیره را مدیریت می کنید.

2.      در گوشه ی سمت چپ، بر روی دکمه ی + کلیک کرده و پوشه ی New Folder را از منوی pop-up انتخاب نمایید.

3.      بر روی اسم پوشه دابل کلیک کرده و آن را به Rating Images تغییر دهید.

4.      پس از انتخاب پوشه ی مذکور، در گوشه ی سمت چپ، بر روی دکمه ی (+) کلیک کرده و سپس گزینه ی New Image Set را از منوی pop-up انتخاب نمایید.

هر Image set به منزله ی یک image asset واحد است اما می تواند نسخه های مختلفی از یک عکس را در وضوح تصویر متفاوت داشته باشد.

5.      بر روی اسم image set دابل کلیک کرده و سپس آن را به emptyStar تغییر دهید.

6.      حال عکس ستاره ی خالی را از حافظه ی کامپیوتر انتخاب نمایید.

7.      عکس را کشیده و آن را در فضای خالی 2x داخل image set جاری جایگذاری نمایید. 

2x وضوح تصویر نمایشگر شبیه ساز iPhone 6 می باشد که برنامه ی خود را در محیط آن تست می کنید. عکس مورد نظر قاعدتا باید در این وضوح با بهترین کیفیت نمایش داده شود.

8.      در پایین محیط، گوشه ی سمت چپ، بر روی دکمه ی (+) کلیک کنید و سپس گزینه ی New Image Set را از منوی pop-up انتخاب نمایید.

9.      بر روی اسم image set دو بار کلیک کرده و آن را به filledStar تغییر دهید.

10.   اکنون عکس ستاره ی پر شده را از حافظه ی کامپیوتر خود انتخاب نمایید.

11.   عکس مورد نظر را کشیده و آن را در فضای خالی 2x، داخل image set جاری جایگذاری نمایید.

Asset catalog شما هم اکنون می بایست مشابه زیر باشد:

در گام بعدی کدی می نویسید که عکس مربوطه را در زمان مناسب برای دکمه تنظیم کرده و نمایش می دهد.

جهت تنظیم عکس برای نمایش، مراحل زیر را به ترتیب دنبال نمایید:

1.      فایل RatingControl.swift را باز کنید.

2.      داخل بدنه ی متد init?(coder:)، دو خط زیر را به قبل از ساختمان حلقه ی for-in اضافه نمایید:

let filledStarImage = UIImage(named: "filledStar")

let emptyStarImage = UIImage(named: "emptyStar")

3.      داخل قطعه کد for-in، پس از آن خطی از کد که دکمه در آن مقداردهی اولیه (initialize) می شود، دستورات زیر را درج کنید:

button.setImage(emptyStarImage, forState: .Normal)

button.setImage(filledStarImage, forState: .Selected)

button.setImage(filledStarImage, forState: [.Highlighted, .Selected])

شما برای هر یک از دو حالت دکمه یک عکس مجزا تخصیص می دهید تا بتوانید به راحتی تشخیص دهید چه زمانی دکمه انتخاب شده و چه زمانی کاربر هنوز بر روی دکمه کلیک نکرده است. عکس ستاره ی خالی زمانی نمایش داده می شود که دکمه انتخاب نشده باشد (وضعیت.Normal ). عکس ستاره ی پر شده زمانی نمایش داده می شود که دکمه انتخاب شده (.Selected ) و همچنین زمانی که دکمه همزمان انتخاب و هایلایت شده باشد (.Selected و .Highlighted= وضعیتی که کاربر در حال ضربه زدن بر روی دکمه باشد.)

4.      آن خطی از برنامه که رنگ پس زمینه ی دکمه را بر روی قرمز تنظیم می کند، حذف نمایید:

button.backgroundColor = UIColor.redColor()

از آنجایی که دکمه های شما اکنون عکس های خود را دارند، زمان آن رسیده تا رنگ پس زمینه ی دکمه را حذف نمایید.

5.      کد زیر را اضافه نمایید:

button.adjustsImageWhenHighlighted = false

دستور فوق را به این خاطر اضافه می کنید که عکس به هنگام تغییر بین دو حالت بیش از یکبار هایلایت نشود.

در حال حاضر کد init?(coder:) شما بایستی به صورت زیر باشد:

required init?(coder aDecoder: NSCoder) {

super.init(coder: aDecoder)

let emptyStarImage = UIImage(named: "emptyStar")

let filledStarImage = UIImage(named: "filledStar")

for _ in 0..<5 {

let button = UIButton()

button.setImage(emptyStarImage, forState: .Normal)

button.setImage(filledStarImage, forState: .Selected)

button.setImage(filledStarImage, forState: [.Highlighted, .Selected])

button.adjustsImageWhenHighlighted = false

button.addTarget(self, action: #selector(RatingControl.ratingButtonTapped(_:)), forControlEvents: .TouchDown)

ratingButtons += [button]

addSubview(button)

}

}

تست کنید: برنامه ی خود را اجرا نمایید. این بار باید بجای مربع های قرمز رنگ، عکس ستاره در UI نمایش داده شوند. با کلیک بر روی هر یک از دکمه ها همچنان فقط متد ratingButtonTapped(_:) صدا خورده می شود و پیغامی در console چاپ می گردد. در این برهه از زمان، با توجه به کدی که برای برنامه نوشته اید، FoodTracker هنوز قادر به تغییر عکس و هایلایت کردن آن به هنگام کلیک کاربر نیست. این رفتار را در گام بعدی برای اپلیکیشن خود تعریف خواهید کرد.  

جایگزین کردن پیاده سازی debugging متد ratingButtonTapped() با پیاده سازی واقعی (پیاده سازی action متصل به UIButton)

برنامه ی شما باید به کاربر اجازه دهد تا با کلیک بر روی ستاره، به غذای مورد نظر امتیاز بدهد. برای این منظور باید پیاده سازی متدratingButtonTapped(_:)  را که در حال حاضر صرفا پیغامی را در console ثبت می کند با عملیات واقعی در بدنه ی متد جایگزین کنید.

جهت پیاده سازی عملیات امتیازدهی در بدنه ی متد ذکر شده، مراحل زیر را دنبال نمایید:

1.      داخل فایل RatingControl.swift، متد ratingButtonTapped(_:) را پیدا کنید:

func ratingButtonTapped(button: UIButton) {

print("Button pressed ")

}

2.      کد زیر را در بدنه ی این متد جایگزین دستور print نمایید:

rating = ratingButtons.indexOf(button)! + 1

متد indexOf(:) دکمه ی مورد نظر را بر اساس اندیس آن در آرایه ای از دکمه ها پیدا کرده و سپس اندیس (شماره ی مکان قرارگیری) آن المان در آرایه را به عنوان خروجی برمی گرداند. از آنجایی که احتمال دارد نمونه ی مورد جستجو در مجموعه ی مورد نظر وجود نداشته باشد، این متد طوری نوشته شده که در خروجی یک عدد صحیح (Int) از نوع optional بازگردانی نماید.

شما می دانید تنها دکمه هایی که action را فعال و سبب اجرای آن می شود، همان دکمه هایی هستند که خودتان تعریف کرده و به آرایه اضافه نمودید. از اینرو مطمئن هستید که متد indexOf به عنوان خروجی شماره ی مکان قرار گیری المان مد نظر را برمی گرداند. می توانید با استفاده از عملگر !، اندیس المان را استخراج نموده و به آن دسترسی داشته باشید. حال جهت بدست آوردن امتیاز مربوطه، عدد 1 را به اندیس المان اضافه می کنید.

توجه داشته باشید که اضافه کردن مقدار 1 ضروری است زیرا اندیس آرایه از 0 آغاز می شود.  

3.      داخل فایل RatingControl.swift، به قبل از آخرین (})، کد زیر را اضافه نمایید:

func updateButtonSelectionStates() {

}

این تابع در واقع یک متد کمکی است که با استفاده از آن وضعیت انتخاب دکمه ها را بروز رسانی می نمایید.  

4.      داخل بدنه ی متد updateButtonSelectionStates()، این حلقه ی for-in را اضافه نمایید:

for (index, button) in ratingButtons.enumerate() {

// If the index of a button is less than the rating, that button should be selected.

button.selected = index < rating

}

این کد داخل آرایه ای از دکمه ها پیمایش کرده، سپس در هر بار تکرار، وضعیت دکمه ها را بر اساس اینکه آیا مقدار اندیس (متغیر index) آن از امتیاز ( مقدار متغیر rating) کوچکتر است یا خیر، تنظیم می کند. در صورت بر قرار بودن شرط، بدین معنی که اگر index < rating نتیجه ی true را بر گرداند، حلقه ی for-in وضعیت دکمه را بر روی selected تنظیم نموده و تصویر ستاره ی پر شده را به نمایش می گذارد. در غیر این صورت، دکمه انتخاب نشده و تصویر ستاره ی خالی به نمایش در می آید.

5.      داخل بدنه ی ratingButtonTapped(_:)، در آخرین خط پیاده سازی، متدupdateButtonSelectionStates() را فراخوانی نمایید:

func ratingButtonTapped(button: UIButton) {

rating = ratingButtons.indexOf(button)! + 1

updateButtonSelectionStates()

}

6.      در بدنه ی متد layoutSubviews()، متد updateButtonSelectionStates() را در آخرین خط پیاده سازی، فراخوانی نمایید:

override func layoutSubviews() {

// Set the button's width and height to a square the size of the frame's height.

let buttonSize = Int(frame.size.height)

var buttonFrame = CGRect(x: 0, y: 0, width: buttonSize, height: buttonSize)

// Offset each button's origin by the length of the button plus some spacing.

for (index, button) in ratingButtons.enumerate() {

buttonFrame.origin.x = CGFloat(index * (buttonSize + 5))

button.frame = buttonFrame

}

updateButtonSelectionStates()

}

لازم است وضعیت انتخاب دکمه هم به هنگام تغییر امتیاز و هم به هنگام بارگذاری view بروز رسانی شود.

7.      در بخش // MARK: Properties، متغیر rating را پیدا کنید:

var rating = 0                                                                                                                                                                                          

8.      اکنون property observer را به متغیر rating اضافه نمایید. property observer تغییرات در مقدار property را رصد کرده و به آن ها واکنش نشان می دهد.

var rating = 0 {

didSet {

setNeedsLayout()

}

}

property observer همان طور که در بالا نیز ذکر شد، یک تکه کد است که به property اضافه شده و تغییرات اعمال شده در آن را دنبال می کند. سپس هربار که مقدار property تنظیم می شود، فراخوانی شده و می توان از آن برای اجرای عملیات بلافاصله قبل/بعد از تغییر مقدار استفاده نمود. به عبارت دقیق تر، didSet یک property observer است که بلافاصله پس از تنظیم مقدار متغیر صدا خورده می شود. در مثال جاری، متد setNeedsLayout() را داخل بدنه ی property observer فراخوانی می کنید که با هر بار تغییر در مقدار متغیر rating، ظاهر/layout را در UI تغییر می دهد. با این کار، اطمینان حاصل می شود که UI همیشه مقدار دقیق متغیر rating را منعکس می کند.

بدنه ی متد updateButtonSelectionStates() هم اکنون بایستی مشابه نمونه ی زیر باشد:

func updateButtonSelectionStates() {

for (index, button) in ratingButtons.enumerate() {

// If the index of a button is less than the rating, that button shouldn't be selected.

button.selected = index < rating

}

}

تست کنید: برنامه ی خود را اجرا کنید. برنامه بایستی 5 ستاره به نمایش گذاشته و شما باید بتوانی با استفاده از آن ها به غذای مورد نظر امتیاز بدهید. به منظور تست، بر روی ستاره ی سوم کلیک کرده تا امتیاز بر روی 3 تنظیم شود.

متصل کردن کنترل امتیاز دهی به کد view controller

آخرین کاری که باید برای تکمیل و راه اندازی کنترل امتیازدهی انجام دهید، این است که یک اشاره گر (در قالب outlet) به این کنترل در کلاس view controller تعریف نمایید.

جهت متصل کردن outlet کنترل امتیازدهی به ViewController.swift، مراحل زیر را به ترتیب دنبال نمایید:

1.      Storyboard خود را باز کنید.

2.      جهت باز کردن ویرایشگر کمکی، بر روی دکمه ی Assistant در نوارابزار محیط کاری Xcode کلیک نمایید:

3.      در صورت نیاز به فضای کاری بیشتر، می توانید project navigator و utility area را با کلیک بر روی دکمه های مربوطه در نوارابزار محیط Xcode، ببندید.  

در صورت لزوم می توانید کادر outline view را نیز پنهان نمایید.

4.      کنترل امتیازدهی را انتخاب نمایید.

محتویات فایل ViewController.swift داخل ویرایشگر کمکی (assistant editor) در سمت راست محیط به نمایش در می آید (اگر محتوای فایل نام برده به نمایش گذاشته نشد، آنگاه می بایست در editor selector bar مراحل روبرو را طی نمایید: Automatic > ViewController.swift ).

5.      بر روی کنترل امتیازدهی کلیک کرده، آن را با اشاره گر موس از canvas به داخل ناحیه ی ویرایشگر بکشید و سپس کنترل مورد نظر را در زیر متغیر photoImageView (داخل فایل ViewController.swift) جایگذاری نمایید.

 

6.      داخل کادر محاوره ای که نمایان می شود، در فیلد Name آن، مقدار ratingControl را وارد نمایید.

لازم نیست به دیگر تنظیمات در این کادر محاوره ای دست بزنید. کادر محاوره ای شما هم اکنون می بایست ظاهری مشابه نمونه ی زیر داشته باشد:

7.      دکمه ی Connect را کلیک نمایید.

اکنون کنترل امتیازدهی در storyboard یک اشاره گر به خود در کلاس ViewController دارد.

پاک سازی کد و اطلاعات غیرضروری از پروژه (Cleanup)

به مراحل نهایی و تکمیل scene جاری در رابط کاربری برنامه ی FoodTracker نزدیک می شوید. اما پیش از تکمیل این صفحه ی محتوا می بایست نسبت به پاک سازی پروژه اقدام نمایید. در حال حاضر برنامه ی FoodTracker رفتار به مراتب پیچیده تری را پیاده سازی کرده و UI متفاوت تری را نسبت به مباحث قبلی به نمایش می گذارد. از اینرو توصیه می شود کدهای غیر ضروری و بلااستفاده را از پروژه حذف نمایید.

در این بخش همچنین المان های رابط کاربری را در stack view وسط چین می کنید تا UI توازن بیشتری پیدا کند.    

جهت پاک سازی لایه ی UI برنامه از کدهای غیرضروری، مراحل زیر را دنبال نمایید:

1.      با کلیک بر روی دکمه ی Standard، ویرایشگر اصلی محیط Xcode (standard editor) را باز نمایید:

2.      حال project navigator و utility area را با کلیک بر روی دکمه های مربوطه در نوارابزار محیط کاری Xcode باز نمایید.

3.      Storyboard را باز نمایید.

4.      دکمه ی Set Default Label Text را انتخاب کنید، سپس با فشردن دکمه ی Delete آن را حذف نمایید.

Stack view با تنظیم مجدد چیدمان المان های UI، فاصله ی خالی که در اثر حذف دکمه ی مورد نظر ایجاد شده را پر می کند.

5.      در صورت لزوم outline view را باز کرده و آبجکت Stack View را از آن انتخاب نمایید.

6.      کادر Attribute inspector را باز کنید.

7.      در Attribute inspector، فیلد Alignment را پیدا کرده و سپس گزینه ی Center را انتخاب نمایید.

المان های موجود در stack view به صورت افقی وسط چین می شوند.  

حال action method متصل به دکمه ی حذف شده را از کد پاک می کنید.

برای این منظور (پاک سازی پروژه از کدهای بلااستفاده)، مراحل زیر را دنبال نمایید:

1.      فایل ViewController.swift را باز کنید.

2.      متد setDefaultLabelText(_:) را از فایل ذکر شده حذف نمایید.

@IBAction func setDefaultLabelText(sender: UIButton) {

mealNameLabel.text = "Default Text"

}

در حال حاضر حذف همین قطعه کد کفایت می کند. در مبحث بعدی تغییراتی را نیز به outlet لیبل اعمال خواهید کرد.

تست کنید: برنامه ی خود را اجرا نمایید. رفتار برنامه نباید نسبت به قبل تغییر کرده باشد. اما این بار دکمه ی Set Default Label Text دیگر وجود ندارد و المان های UI به صورت افقی در وسط صفحه بر روی هم قرار گرفته اند. علاوه بر آن دکمه ها بایستی در یک سطر و کنار هم چیده شده باشند. با کلیک بر روی هر یک از دکمه ها، همچنان متدratingButtonTapped(_:) صدا خورده می شود اما با توجه به تغییراتی که در کد برنامه اعمال کردید، این بار عکس دکمه ها به دنبال اجرای متد نام برده، تغییر می کنند.

مهم: چنانچه در زمان build/کامپایل با مشکل مواجه شدید، می توانید کلیدهای Command-Shift-K را جهت پاک سازی پروژه فشار دهید.

newer

آموزش کار با view controller ها

در این مبحث همچنان بر روی scene و UI مربوطه ی آن در برنامه ی ثبت و مشاهده ی اطلاعات غذا،FoodTracker، کار خواهید کرد. ترتیب المان های جاری UI را تغییر داده و با استفاده از image picker یک عکس به رابط کاربری برنامه ی خود اضافه خواهید نمود.

در پایان برنامه ظاهری مشابه زیر خواهد داشت:

clip_image002

آنچه خواهید آموخت

1.      با چرخه ی حیات view controller و مفهوم آن آشنا شده، بدانید توابع بازفراخوان/callback های آن، viewDidLoad، viewWillAppear و viewDidAppear چه زمانی صدا خورده و اجرا می شوند. 

2.      بتوانید بین view controller ها داده رد و بدل نمایید.

3.      یک view controller را از صفحه حذف یا پنهان کنید.

4.      با الحاق کردن آبجکت های gesture recognizer به view، قابلیت تعامل با کاربر و واکنش نشان دادن به رخداد/event ها را به آن اعطا نمایید.

5.      بر اساس سلسله مراتب و زنجیره ی ارث بری (class hierarchy) UIView/UIControl، رفتار یک آبجکت را پیش بینی کنید.

6.      با استفاده از ابزار asset catalog، محتوا و منابعی همچون عکس را به پروژه اضافه نمایید.

آشنایی با life cycle/چرخه ی حیات View Controller و مفهوم آن

در حال حاضر، برنامه ی FoodTracker از تنها یک scene یا صفحه ی محتوا برخوردار است که UI آن توسط فقط یک view controller مدیریت می شود. با ساخت اپلیکیشن های پیچیده تر، نا گزیر با scene های بیشتری سر و کار داشته و علاوه بر آن می بایست بارگذاری و حذف view ها از حافظه را همین که بر روی صفحه نمایش داده و سپس از آن حذف می شوند، اداره نمایید.  

آبجکتی که از روی کلاس UIViewController (و کلاس های فرزند آن) ساخته می شود، تعدادی متد پیش فرض دارد که توسط آن ها view hierarchy (نمای سلسله مراتبی از آبجکت ها/view های یک scene همچون دکمه، لیبل) خود را مدیریت می کند. زمانی که یک view controller تغییر وضعیت می دهد (برای مثال در صفحه نمایش داده می شود و سپس از آن حذف می گردد)، IOS خود این متدها را در زمان مناسب فراخوانی می کند. 

کلاس فرزندی که از UIViewController ایجاد می کنید، مانند کلاس ViewController که در برنامه ی جاری با آن کار می کنید، تمامی متدهای کلاس والد (UIViewController) را به ارث برده و در این حین به شما امکان می دهد تا کد خود را جهت پیاده سازی عملیات و رفتار دلخواه به بدنه ی این متدها اضافه نمایید. به منظور تنظیم و آماده سازی view ها جهت نمایش در صفحه یا حذف آن ها در زمان مناسب (یا گام مربوطه در فرایند مزبور)، آگاهی از ترتیب و زمان دقیق فراخوانی متدهای نام برده ضروری است. البته این عملیات کمی پیچیده بوده و در مباحث آینده به پیاده سازی آن خواهید پرداخت.

clip_image004

متدهای کلاس UIViewController به ترتیب شرح داده شده در زیر صدا خورده و اجرا می شوند:

·         viewDidLoad() – زمانی فراخوانی می شود که content view (view ای که در بالای سلسله مراتب قرار گرفته و چندین view دیگر را دربرمی گیرد) کلاس view controller ایجاد شده و از storyboard بارگذاری می شود. این متد ویژه ی تنظیم اولیه تعبیه شده است. لازم به ذکر است که view ها، گاهی به دلیل عدم وجود منابع کافی، از برنامه حذف می شوند و از اینرو هیچ تضمینی وجود ندارد که متد مذکور تنها یکبار فراخوانده شود (ممکن است به دلیل منابع محدود و حذف view ها از صفحه، لازم باشد چندین بار فراخوانی شود).

·         viewWillAppear() ویژه ی عملیاتی طراحی شده که می خواهید حتما پیش از به نمایش گذاشتن  view برای کاربر، انجام شوند. از آنجایی که امکان دارد قابلیت رویت یک view به دلیل وجود دیگر view ها، بین دو حالت تغییر کند (نمایش داده شده/پنهان شود) یا کلا تحت الشعاع قرار گیرد، این متد همیشه بلافاصله قبل از نمایان شدن content view بر روی صفحه صدا زده می شود.

·         viewDidAppear() – عملیاتی که می خواهید بلافاصله پس از نمایان شدن view بر روی صفحه، انجام شوند از جمله واکشی داده ها و به اجرا گذاشتن یک انیمیشن و غیره ... در بدنه ی این متد تعریف می شوند. از آنجایی که امکان دارد قابلیت رویت یک view به دلیل وجود دیگر view ها، بین دو حالت تغییر کند (نمایش داده شده/پنهان شود) یا کلا تحت الشعاع قرار گیرد، این متد همیشه بلافاصله پس از نمایان شدن content view بر روی صفحه صدا زده می شود. 

همان طور که در نمودار فوق مشاهده می کنید، تعدادی متد مکمل نیز جهت teardown یا حذف view از صفحه وجود دارد.

برخی از متدهای ذکر شده را جهت بارگذاری و نمایش داده های view در زمان مناسب، در برنامه ی FoodTracker مورد استفاده قرار خواهید داد. در واقع، اگر بخاطر داشته باشید، داخل بدنه ی یکی از این متدها (تابع viewDidLoad() از کلاس ViewController) کدنویسی کردید:

override func viewDidLoad() {

super.viewDidLoad()

// Handle the text field’s user input through delegate callbacks.

nameTextField.delegate = self

}

این سبک طراحی برنامه که در آن view controller ها به عنوان پل های ارتباطی (pipeline) بین view ها و data model ایفای نقش کرده و زمینه ی تعامل بین آن ها را فراهم  می نمایند، در اصطلاح معماری MVC ( الگو توسعه ی مدل-نما-کنترلگر) خوانده می شود. در این الگوی توسعه، model وظیفه ی کار با داده ها (ارتباط با پایگاه داده/هر منبع داده ای همچون آرایه و واکشی اطلاعات از آنها، ذخیره ی موقت یا تبدیل آن ها به یک شی و همچنین بررسی صحت داده ها) را بر عهده دارد، بخش view ظاهر برنامه/UI را در صفحه به نمایش گذاشته، محتوای قابل مشاهده برای کاربر را ارائه می دهد و در نهایت لایه ی controller اداره ی view ها، مدیریت تعامل با کاربر را عهده دار بوده و در حقیقت واسطی میان model و view محسوب می شود. با واکنش نشان دادن به اعمال یا ورودی کاربر، پر کردن view ها با داده از data model، بخش controller به مثابه ی یک درگاه یا پل ارتباطی بین لایه ی model و view ایفای نقش می کند. در حال حاضر تمامی برنامه های کارامد و پربازده ی IOS مبتنی بر این الگوی توسعه طراحی می شوند و برنامه ی FoodTracker نیز از این امر مستثنی نیست.    

در بخش بعدی UI ساده برنامه را گسترش داده و قالب یا طرح نهایی (layout) آن را مشخص می کنید.

افزودن یک عکس به UI برنامه

مرحله بعدی که آخرین گام در تکمیل ظاهر برنامه ی Foodtracker است، تعریف مکانیزمی جهت نمایش یک عکس، ویژه ی غذای مورد نظر در UI می باشد. برای این منظور از یک image view (کلاس UIImageView) بهره می گیرد. Image view یک المان UI است که تصویری را برای کاربر بر روی صفحه به نمایش می گذارد. 

به منظور افزودن image view به scene فعلی مراحل زیر را گام به گام دنبال نمایید:

1.      فایل storyboard خود، Main.storyboard را از project navigator انتخاب کرده و باز نمایید.

2.      Object library را از utility area باز نمایید (یا این مراحل را دنبال نمایید: View > Utilities > Show Object Library).

 

clip_image006

3.      داخل فیلد جستجوی Object library، عبارت image view را وارد نمایید تا Xcode به سرعت آبجکت Image View را یافته و در اختیار شما قرار دهد.

4.      آبجکت نام برده را از Object library کشیده و در سطح scene جاری، زیر کنترل دکمه جایگذاری کنید.

clip_image008

5.      پس از کلیک بر روی المان image view و انتخاب آن، کادر Size inspector را در utility area باز کنید. اگر بخاطر داشته باشید، Size inspector با کلیک بر روی دکمه ی پنجم از سمت چپ در inspector selector bar، به نمایش در می آید. این کادر به شما اجازه ی تنظیم اندازه و مکان قرار گیری آبجکت مورد نظر در storyboard را می دهد.

clip_image010

6.      از فیلد Intrinsic Size در پایین کادر، گزینه ی Placeholder را انتخاب نمایید.

7.      داخل فیلدهای Width و Height، مقدار 320 را وارد کنید. کلید Return را فشار دهید.

به دلیل اینکه در image view حاضر عکسی برای نمایش وجود ندارد، از intrinsic content size آن (حداقل فضای لازم برای نمایش کامل و بی نقص محتوای view) اطلاع کافی نداریم. بنابراین فیلد Intrinsic Size را بر روی گزینه ی placeholder تنظیم می کنیم تا بعده ها بتوانیم اندازه ی صحیح و constraint های مناسب را در interface builder مشخص کنیم.    

clip_image012

8.      در زیر canvas (پس زمینه ی storyboard)، گوشه ی سمت راست محیط، منوی Pin را با کلیک بر روی سومین دکمه از سمت چپ باز نمایید.

clip_image014

9.      در کادر جاری، چک باکس Aspect Ratio را تیک دار نمایید. هم اکنون منوی Pin باید مشابه زیر باشد:


clip_image016

10.   در منوی Pin، بر روی دکمه ی Add 1 Constraint کلیک نمایید.

clip_image018

Image view در حال حاضر دارای aspect ratio/نسبت پهنا به ارتفاع 1:1 می باشد. بنابراین یک مربع را برای کاربر به نمایش می گذارد.

11.   پس از کلیک بر روی المان image view و انتخاب آن، کادر Attribute inspector را باز کنید.

اگر بخاطر داشته باشید، کادر Attribute inspector با کلیک بر روی دکمه ی چهارم از سمت چپ در inspector selector bar باز می شود. در این کادر شما می توانید property های یک آبجکت را در storyboard خود ویرایش کنید.

12.   در کادر Attribute inspector، فیلدی که Interaction نام دارد را پیدا کرده و سپس چک باکس User Interaction Enabled را تیک دار نمایید. این امکان به کاربران اجازه می دهد تا با المان image view تعامل داشته باشند.

اپلیکیشن شما اکنون باید ظاهری مشابه زیر داشته باشد:

clip_image020

به نمایش گذاشتن یک عکس مکان نگهدار در image view

شما باید به طریقی به کاربر اعلان کنید که اجازه دارد با المان image view تعامل داشته و از آن یک عکس را انتخاب و مشاهده نماید. برای نیل به این هدف، یک عکس به عنوان مکان نگهدار در این المان به نمایش می گذارید که کاربر با مشاهده ی آن متوجه می شود که می تواند با آن تعامل داشته و تصویری را برای مشاهده انتخاب کند.

clip_image022

می توانید این تصویر را در پوشه ی  Images/ از پروژه ی حاضر (پس از دانلود آن) انتخاب کنید یا عکس دلخواه خود را انتخاب نمایید.

به منظور افزودن یک عکس به پروژه ی خود مراحل زیر را دنبال نمایید:

1.      در project navigator، با کلیک بر روی فایل Assets.xcassets، ابزار asset catalog را باز کنید. asset catalog بستری است که در آن asset های پروژه (محتوای برنامه ی خود همچون عکس ها، فایل های تصویری و متنی) را ذخیره و سازمان دهی می کنید.

2.      در پایین محیط کاری Xcode، گوشه ی سمت چپ بر روی دکمه ی (+) کلیک کرده و سپس گزینه ی New Image Set را از منوی pop-up انتخاب نمایید.      

clip_image024

3.      بر روی اسم image set دوبار کلیک کرده و آن را به defaultPhoto تغییر دهید.

4.      عکس دلخواه را از حافظه ی کامپیوتر خود انتخاب کنید.

5.      حال عکس را کشیده و آن را در فضای خالی 2x در image set جایگذاری کنید.

clip_image026

 

2x ، وضوح تصویر نرم افزار شبیه ساز/simulator iPhone 6 در محیط Xcode است. از اینرو عکس مورد نظر باید با بهترین کیفیت به نمایش گذاشته شود.  

پس از افزودن عکس مکان نگهدار به پروژه، بایستی تنظیمات image view را انجام دهید تا این المان بتواند عکس مورد نظر را در UI برای کاربر به نمایش بگذارد.

جهت نمایش عکس مکان نگهدار در المان image view، مراحل زیر را گام به گام دنبال نمایید:

1.      فایل storyboard را باز کنید.

2.      در storyboard، المان image view را انتخاب نمایید.

3.      پس از کلیک بر روی المان مورد نظر، کادر Attributes inspector را از utility area باز کنید.

4.      در Attribute inspector، فیلدی که Image نام دارد را پیدا کرده و سپس گزینه ی defaultPhoto را از آن انتخاب نمایید.  

تست کنید: برنامه را در شبیه ساز اجرا کنید. می بینید که عکس مکان نگهدار در المان image view به نمایش در می آید.  

clip_image027

 

 

متصل کردن المان image view به کد مربوطه در فایل ViewController.swift

در این بخش بایستی با کدنویسی قابلیتی به المان image view اضافه کنید که به شما اجازه می دهد محتوای آن را در زمان اجرای برنامه (runtime)، تغییر دهید. شما باید بتوانید تصویر را از داخل کد (با کدنویسی) تغییر دهید. برای این منظور ابتدا لازم است المان image view را به کد مربوطه در فایل ViewController.swift متصل کنید.   

جهت متصل کردن المان image view به کد مرتبط در فایل ViewController.swift، مراحل زیر را گام به گام دنبال نمایید:

1.      بر روی دکمه ی Assistant در نوار ابزار Xcode، بالای محیط گوشه ی سمت راست کلیک نموده و ویرایشگر کمکی (assistant editor) را باز کنید.  

clip_image029

2.      در صورت نیاز به فضای کاری بیشتر، با کلیک بر روی دکمه های navigator toggle و utility toggle در نوار ابزار Xcode، می توانید project navigator و utility area را ببندید. 

clip_image031

در صورت تمایل، می توانید کادر outline view را نیز ببندید.

3.      در storyboard، المان image view را انتخاب نمایید. 

4.      حال المان نام برده را از سطح scene جاری کشیده و در ناحیه ی ویرایش کد (سمت راست محیط، داخل فایل ViewController.swift) در زیر outlet جاری (متغیر mealNameLabel) جایگذاری نمایید. 

clip_image033

5.      در پنجره ی محاوره ای که به نمایش در می آید، واژه ی photoImageView را وارد فیلد Name نمایید. لازم نیست سایر تنظیمات را دستکاری کنید. پنجره ی محاوره ای هم اکنون می بایست به صورت زیر باشد:

clip_image035

6.      حال بر روی دکمه ی Connect کلیک نمایید.

Xcode یک اشاره گر در قالب متغیری با نام photoImageView به المان مزبور در فایل ViewController.swift ایجاد می کند. سپس storyboard را جهت برقراری اتصال بین کد و المان مربوطه در UI تنظیم می نماید.

@IBOutlet weak var photoImageView: UIImageView!

حال می توانید از کد به المان image view در UI دسترسی داشته و محتوای آن را تغییر دهید. اما اینجا یک سوال جالب مطرح می شود! از کجا بفهمیم چه زمانی باید عکس یا محتوای المان مزبور را تغییر دهیم؟ شما می بایست روشی تعریف کنید که کاربران از طریق آن بتوانند تمایل خود مبنی بر تغییر محتوای image view را به نحوی بیان کنند، برای مثال با کلیک (قرار دادن انگشت) بر روی image view.  

پس از آن یک action method تعریف می کنید که به مجرد رخداد tap (کلیک کاربر بر روی المان)، صدا خورده شده و محتوای المان را تغییر می دهد.

گفتنی است که بین view و control یک تفاوت جزئی وجود دارد. همان طور که می دانید control ها نسخه های ویژه از view ها هستند که به شیوه ای خاص به ورودی یا اعمال کاربر واکنش نشان می دهند. در واقع view صرفا محتوای ساده ای را برای کاربر به نمایش می گذارد، در حالی که یک control امکان ویرایش محتوا (به نحوی خاص) را نیز در اختیار کاربر قرار می دهد.

control (کلاس UIControl) در حقیقت از کلاس UIView ارث بری می کند (یک کلاس فرزند از UIView است). از نمونه های view می توان به label و image view اشاره کرده و از control ها می توان از text field و button نام برد که اگر بخاطر داشته باشید قبلا با تمامی آن ها در UI برنامه ی خود کار کرده اید. 

تعریف یک Gesture Recognizer (اعطای قابلیت های یک control به view)

همان طور که در بالا گفته شد، المان image view از نوع control نیست، بنابراین واکنشی که در پاسخ به ورودی کاربر نشان می دهد با یک control –همچون دکمه – یکسان نیست (در اصل طوری طراحی نشده که به عمل کاربر مانند یک کنترل واکنش نشان دهد). به عنوان مثال، نمی توانید به راحتی و بدون هیچ گونه واسطی یک action method تعریف کرده، آن را به image view متصل کنید که به مجرد کلیک کاربر بر روی المان مذکور مستقیما فراخوانی/اجرا می شود (اگر بر روی المان در interface builder کلیک کرده و آن را به ناحیه ی ویرایش کد بکشید، می بینید که داخل کادر محاوره ای امکان انتخاب گزینه ی Action از فیلد connection وجود ندارد).  

خوشبختانه، به راحتی می توانید قابلیت های یک control را به view مورد نظر اعطا کنید. برای این منظور کافی است از gesture recognizer استفاده نمایید. gesture recognizer ها آبجکت هایی هستند که با الصاق آن ها به view، به آن ها این امکان داده می شود تا درست مانند یک control به action یا فعل کاربر واکنش نشان دهند.

Gesture recognizer ها حالات مختلف لمس را شناسایی کرده و هر یک را به فعل خاصی تفسیر می کنند. در واقع زمانی که کاربر نمایشگر را با حالت خاصی لمس می کند، IOS آن را مورد بررسی و تحلیل قرار می دهد. سپس آن حالت خاص لمس را معادل یکی از حالات تعریف شده برای سیستم عامل مورد نظر همچون pinch، swipe یا rotation در نظر می گیرد. در پی آن، متناسب با حالت لمس تفسیر شده، (پس از فراخوانی متد متصل به آن gesture recognizer) عملیات معینی نظیر کوچک/بزرگ کردن صفحه یا چرخاندن تصویر را ترتیب می دهد.

جهت اعطا کردن قابلیت های یک control به view (واکنش نشان دادن به عمل کاربر)، کافی است یک آبجکت gesture recognizer به آن view متصل کنید. سپس یک action method تعریف کنید که به مجرد اینکه gesture recognizer حالت لمس کاربر را شناسایی و تفسیر کرد، آن متد فراخوانی شود.

 در این بخش با الصاق یک gesture recognizer به المان image view، قابلیت تعامل با کاربر را به این المان اعطا می کنیم.

ابتدا یک tap gesture recognizer (از کلاس UITapGestureRecognizer) به المان image view الحاق کنید. با این کارِ شما، زمانی که کاربر با انگشت خود به سطح نمایشگر ضربه می زند، gesture recognizer آن را شناسایی کرده و پس از تفسیر آن (و فراخوانیaction method  مربوطه) زمینه ی تعامل کاربر با المان مزبور را فراهم می آورد.

جهت افزودن یک tap gesture recognizer به المان image view در storyboard، مراحل زیر را گام به گام دنبال نمایید:  

1.      در مرحله ی اول، Object library را باز کنید. (روش سریع برای باز کردن آن: View > Utilities > Show Object Library.)

2.      داخل فیلد جستجوی Object library، واژه ی tap gesture را وارد کنید تا محیط Xcode آبجکت مد نظر را یافته و به سرعت در اختیار شما قرار دهد. 

3.      آبجکت Tap Gesture Recognizer را از Object library کشیده و آن را بر روی المان image view در scene کنونی جایگذاری نمایید.

Attach a tap gesture recognizer (UITapGestureRecognizer) to the image view, which will recognize when a user has tapped the image view. You can do this easily in your storyboard.

clip_image037

می بینید که آیکون Tap Gesture Recognizer در scene dock نمایان می شود (نواری در بالای scene که اطلاعاتی را درباره ی آن به نمایش می گذارد).

clip_image039

متصل کردن gesture recognizer به کد مربوطه در فایل ViewController.swift

اکنون زمان آن فرا رسیده که gesture recognizer را به action method در کد متصل کنید.

به منظور متصل کردن gesture recognizer به کد مربوطه در فایل ViewController.swift، مراحل زیر را دنبال نمایید:

1.      بر روی آیکون gesture recognizer در scene dock کلیک کنید، آن را به ناحیه ی ویرایش کد در سمت راست scene جاری کشیده و در زیر خط // MARK: (خط توضیحات مربوط به action ها در فایل ViewController.swift) جایگذاری نمایید:

clip_image041

2.      در کادر محاوره ای که نمایان می شود، گزینه ی Action را از فیلد Connection انتخاب کنید.

3.      در فیلد Name، واژه ی selectImageFromPhotoLibrary را به عنوان اسم متد وارد کنید.

4.      از فیلد Type، گزینه ی UITapGestureRecognizer را انتخاب نمایید. پس از تنظیمات ذکر شده، کادر محاوره ای می بایست به صورت زیر باشد:

clip_image043

5.      حال بر روی دکمه ی Connect کلیک نمایید.

Xcode خود کد مورد نیاز جهت (ایجاد و تنظیم) action method را به فایل ViewController.swift اضافه می کند.

@IBAction func selectImageFromPhotoLibrary(sender: UITapGestureRecognizer) {

}

 

 

افزودن یک image picker به پروژه جهت تعامل با کاربر و فراهم آوردن امکان انتخاب عکس

زمانی که کاربر بر روی المان image view در نمایشگر دستگاه ضربه می زند، طبیعتا باید بتواند یک عکس از مجموعه عکس های آماده در حافظه ی دستگاه انتخاب کند یا عکس دلخواه خود را بگیرد. خوشبختانه، کلاس UIImagePickerController تمامی این رفتارها را به صورت درون ساخته در اختیار دارد.

image picker controller رابط کاربری مورد نیاز جهت گرفتن عکس جدید یا انتخاب از میان عکس های ذخیره شده در حافظه ی دستگاه و استفاده از آن ها در برنامه ی جاری را مدیریت می کند. همان طور که به هنگام کار با المان text field به یک delegate احتیاج دارید، برای کار با image picker نیز، ناگزیر به یک delegate image picker controller نیاز دارید. protocol (الگوی پیاده سازی) ویژه ی این delegate عبارت است از UIImagePickerControllerDelegate. همچنین، آبجکتی که به عنوان delegate آبجکت image picker منصوب می کنید (آبجکتی که با همکاری و هماهنگی آن )، مانند نمونه ی قبلی، ViewController است.  

اما پیش از هر چیزی، ViewController می بایست protocol نام برده (UIImagePickerControllerDelegate) را پیاده سازی کند. از آنجایی که ViewController وظیفه ی ارائه ی image picker controller را نیز بر عهده دارد، ناگزیر می بایست پروتکل UINavigationControllerDelegate را هم پیاده سازی کند. این protocol برخی از مسئولیت های پیمایش (navigation) را به view controller واگذار می کند.     

به منظور افزودن protocol های نام برده به ViewController، مراحل زیر را دنبال نمایید:

1.      با کلیک بر روی دکمه ی Standard در سمت چپ نوار ابزار Xcode، ویرایشگر اصلی محیط (standard editor) را باز کنید.

clip_image045

حال project navigator و utility area را با کلیک بر روی دکمه های مربوطه ی آن ها (اشاره شده در تصویر فوق) باز نمایید.

2.      از کادر project navigator، فایل ViewController.swift را انتخاب نمایید.

3.      داخل فایل ViewController.swift، خط حامل تعریف کلاس را پیدا کنید:

class ViewController: UIViewController, UITextFieldDelegate {

4.      بعد از UITextFieldDelegate، یک ویرگول اضافه کرده و سپس UINavigationControllerDelegate را درج کنید.

class ViewController: UIViewController, UITextFieldDelegate, UIImagePickerControllerDelegate {

5.      اکنون پس از UIImagePickerControllerDelegate، یک ویرگول اضافه کرده و سپس UINavigationControllerDelegate را تایپ کنید. با این کار کلاس ViewController را ملزم به پیاده سازی متدهای تعریف شده در این دو protocol می کنید:

class ViewController: UIViewController, UITextFieldDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {

پس از اضافه کردن این دو protocol به خط تعریف کلاس، می توانید به تکمیل پیاده سازی متد selectImageFromPhotoLibrary(_:) بپردازید.

جهت پیاده سازی متد selectImageFromPhotoLibrary(_:)، مراحل زیر را دنبال کنید:

1.      در فایل ViewController.swift، متد selectImageFromPhotoLibrary(_:) را پیدا کنید. بایستی ظاهری مشابه زیر داشته باشد:

@IBAction func selectImageFromPhotoLibrary(sender: UITapGestureRecognizer) {

}

در بدنه ی متد، بین ({}) کد زیر را اضافه کنید:

// Hide the keyboard.

nameTextField.resignFirstResponder()

این کد سبب می شود اگر کاربر حین نمایان بودن صفحه کلید در نمایشگر، بر روی المان image view کلیک کرد، صفحه کلید به طور کامل از صفحه حذف شود.

2.      حال کد زیر را جهت ایجاد یک image picker controller، به بدنه ی متد اضافه نمایید:

// UIImagePickerController is a view controller that lets a user pick media from their photo library.

let imagePickerController = UIImagePickerController()

3.      سپس کد زیر را به بدنه ی متد اضافه کنید:

// Only allow photos to be picked, not taken.

imagePickerController.sourceType = .PhotoLibrary

این خط کد، محلی که image picker controller فایل های تصویری خود را از آن وارد المان می کند را مشخص می نماید. پارامتر .PhotoLibrary به image picker اعلان می کند که باید عکس های خود را فقط از مجموعه تصاویر محیط شبیه ساز (simulator camera roll) وارد کند. می دانیم که imagePickerController.sourceType از جنس UIImagePickerControllerSourceType است که نوع داده ای آن enumeration می باشد. بنابراین می توانید بجای اینکه مقدار را به طور کامل UIImagePickerControllerSourceType.PhotoLibrary تایپ کنید، آن را به صورت مختصر .PhotoLibrary مورد اشاره قرار دهید. یادآور می شویم که هرگاه نوع مقدار enumeration از قبل مشخص است، می توانید از صورت مختصر آن استفاده نمایید.    

4.      با افزودن کد زیر، کلاس حاضر (ViewController) را به عنوان delegate کنترلرimage picker  انتخاب نمایید:

// Make sure ViewController is notified when the user picks an image.

imagePickerController.delegate = self

5.      حال این کد را در زیر کد فوق، به بدنه ی متد اضافه کنید:

presentViewController(imagePickerController, animated: true, completion: nil)

presentViewController(_:animated:completion:) یک متد است که در سطح کلاس ViewController فراخوانی می شود. اگرچه این متد صریحا به آبجکت self الحاق نشده، اما از شرایط جاری مشخص است که بر روی آن فراخوانی می شود. متد مذکور به کلاس ViewController اعلان می کند که باید view controller را نمایش دهد.

ارسال مقدار true به پارامتر animated، سبب می شود image picker controller به صورت پویا (با انیمیشن) ارائه شود.پارامتر completion به یک completion handler اشاره دارد. Completion handler یک تابع closure است که با اتمام اجرای متد جاری، صدا زده می شود. از آنجایی که پس از اجرای کامل متد، نیاز به انجام عملیات دیگری نیست، مقدار nil را به این پارامتر پاس می دهیم.

در حال حاضر بدنه ی متدselectImageFromPhotoLibrary(_:) بایستی مشابه نمونه ی زیر باشد:

@IBAction func selectImageFromPhotoLibrary(sender: UITapGestureRecognizer) {

// Hide the keyboard.

nameTextField.resignFirstResponder()

// UIImagePickerController is a view controller that lets a user pick media from their photo library.

let imagePickerController = UIImagePickerController()

// Only allow photos to be picked, not taken.

imagePickerController.sourceType = .PhotoLibrary

// Make sure ViewController is notified when the user picks an image.

imagePickerController.delegate = self

presentViewController(imagePickerController, animated: true, completion: nil)

}

پس از اینکه image picker controller به نمایش گذاشته شد، رفتارش (عملیاتی که قادر به اجرای آن ها است) به آبجکت delegate محول می شود.

برای این که به کاربران این امکان را بدهید تا پس از کلیک بر روی المان، یک عکس را انتخاب کنند، لازم است دو متد زیر از پروتکل UIImagePickerControllerDelegate را پیاده سازی نمایید:   

func imagePickerControllerDidCancel(picker: UIImagePickerController)

func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject])

متد اول، imagePickerControllerDidCancel(_:)، زمانی صدا خورده می شود که کاربر بر روی دکمه ی Cancel از image picker کلیک می کند. این متد در واقع به شما اجازه می دهد تا UIImagePickerController را از صفحه حذف/محو کنید (و در صورت تمایل، کدی اضافه کنید که عملیات cleanup و پاک سازی داده های غیرضروری از حافظه را انجام می دهد).

جهت پیاده سازی متد imagePickerControllerDidCancel(_:)، مراحل زیر را به ترتیب دنبال نمایید:

1.      داخل فایل ViewController.swift، درست در بالای // MARK: Actions ، خط زیر را درج نمایید:

1.       // MARK: UIImagePickerControllerDelegate

این کد صرفا یک comment است که شما یا هر شخص دیگری که کد برنامه ی شما را می خواند را در پیمایش در پروژه و فهم کاربرد بخش های مختلف آن یاری می کند. این comment اعلان می کند که این بخش از کد مربوط به پیاده سازی متدهای image picker می باشد.

2.      حال در زیر comment، متد زیر را وارد نمایید:

func imagePickerControllerDidCancel(picker: UIImagePickerController) {

}

3.      داخل بدنه ی متد دستور زیر را تایپ کنید:

// Dismiss the picker if the user canceled.

dismissViewControllerAnimated(true, completion: nil)

این کد، فرایند حذف شدن image picker controller از نمایشگر را پویانمایی می کند (با انیمیشن آن را نظر کاربر پنهان می کند).

بدنه ی متد imagePickerControllerDidCancel(_:) هم اکنون بایستی مشابه نمونه ی زیر باشد:

func imagePickerControllerDidCancel(picker: UIImagePickerController) {

// Dismiss the picker if the user canceled.

dismissViewControllerAnimated(true, completion: nil)

}

دومین متدی که باید از پروتکل UIImagePickerControllerDelegate پیاده سازی کنید، imagePickerController(_:didFinishPickingMediaWithInfo:) می باشد. این متد زمانی فرخوانی می شود که کاربر عکس دلخواه خود را انتخاب می نماید.

با اجرا شدن متد نام برده، این فرصت در اختیار شما قرار می گیرد تا بر روی عکس یا عکس هایی که کاربر انتخاب کرده، عملیات مورد نظر را انجام دهید. در مثال جاری، شما عکس مورد انتخاب کاربر را گرفته و در UI به نمایش می گذارید.  

جهت پیاده سازی متد imagePickerController(_:didFinishPickingMediaWithInfo:)، مراحل زیر را به ترتیب دنبال نمایید:

1.      در زیر متد imagePickerControllerDidCancel(_:)، این کد را درج نمایید:

func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {

}

2.      در بدنه ی متد، کد زیر را وارد نمایید:

// The info dictionary contains multiple representations of the image, and this uses the original.

let selectedImage = info[UIImagePickerControllerOriginalImage] as! UIImage

info در کد فوق، یک dictionary است که علاوه بر عکس اولیه و انتخاب شده در image picker، نسخه ی ویرایش شده ی آن را نیز دربر می گیرد. در اینجا از نسخه ی ساده و ویرایش نشده ی عکس برای غذای مورد نظر استفاده می کنیم. کد حاضر نیز همین عکس (نسخه ی ویرایش نشده) را داخل ثابت selectedImage ذخیره می کند.

3.      این کد را به بدنه ی متد اضافه کنید تا عکس مورد نظر در متغیر photoImageView (یک outlet که قبلا به فایل اضافه کردید) قرار داده شده و برای کاربر به نمایش درآید.

// Set photoImageView to display the selected image.

photoImageView.image = selectedImage

4.      حال با افزودن کد زیر به متد، image picker را از حالت نمایش خارج کنید:

// Dismiss the picker.

dismissViewControllerAnimated(true, completion: nil)

در حال حاضر بدنه ی متد imagePickerController(_:didFinishPickingMediaWithInfo) شما بایستی مشابه زیر باشد: 

func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {

// The info dictionary contains multiple representations of the image, and this uses the original.

let selectedImage = info[UIImagePickerControllerOriginalImage] as! UIImage

// Set photoImageView to display the selected image.

photoImageView.image = selectedImage

// Dismiss the picker.

dismissViewControllerAnimated(true, completion: nil)

}

تست کنید: برنامه را اجرا کنید. بایستی پس از کلیک بر روی image view، یک image picker به نمایش درآمده و به شما اجازه ی انتخاب عکس دلخواه را بدهد. برای اینکه برنامه FoodTracker بتواند به بخش Photos دسترسی داشته و به کاربر اجازه ی انتخاب عکس را بدهد، باید بر روی دکمه ی OK در کادر محاوره ای کلیک نمایید. پس از آن، با توجه به کدی که برای برنامه نوشته شده، باید بتوانید با کلیک بر روی Cancel، انتخابگر عکس (image picker) را ببندید یا Camera Roll را باز کرده، عکس دلخواه را از آن انتخاب نمایید و متعاقبا در image view به نمایش بگذارید.

clip_image047

اگر به عکس های موجود در محیط شبیه ساز (Simulator) نگاه کنید، متوجه می شوید که عکسی در رابطه با غذا در آن وجود ندارد. می توانید عکس های مد نظر خود را مستقیما وارد محیط شبیه ساز نموده و در image view نمایش دهید یا پس از دانلود پروژه ی این آموزش، به پوشه ی Images/ مراجعه نموده و از عکس نمونه ی داخل این فولدر استفاده کنید.  

جهت افزودن عکس جدید به شبیه ساز، مراحل زیر را دنبال نمایید:

1.      ابتدا برنامه را در محیط شبیه ساز اجرا کنید.

2.      عکس دلخواه را انتخاب نمایید.

3.      عکس مورد نظر را با اشاره گر موس کشیده و در محیط شبیه ساز جایگذاری کنید.

connect to ui

متصل کردن UI (ظاهر یا رابط کاربری برنامه) به کد (رفتارها و عملیاتی که برنامه انجام می دهد)

در این مبحث، شما UI ساده ی اپلیکیشن FoodTracker (یا ثبت و رصد اطلاعات غذا) را به کد برنامه متصل می نمایید. با استفاده از این کد امکان انجام عملیات خاصی بر روی UI را برای کاربر برنامه ی خود فراهم می کنید.

در پایان برنامه ظاهری مشابه زیر خواهد داشت:

 

 

clip_image002[6]

آنچه خواهید آموخت

·         رابطه ی بین یک scene در storyboard و view controller زیرین آن را درک کرده و توضیح دهید.

·         با استفاده از outlet و action بین المان های UI در سطح storyboard و کد برنامه (source code) ارتباط برقرار کنید.

·         ورودی کاربر از یک کادر متن یا text field را خوانده، پردازش کنید و سپس خروجی آن را در UI اپلیکیشن به نمایش بگذارید.

·         یک کلاس را بر اساس protocol خاص پیاده سازی کنید (property ها و متدهای تعیین شده توسط آن protocol را در کلاس پیاده سازی کنید).

·         با الگوی توسعه ی delegation آشنا شوید.

·         در طراحی معماری اپلیکیشن از الگوی توسعه ی target-action پیروی نمایید (الگوی توسعه ی target-action را در معماری اپلیکیشن پیاده سازی کنید).

متصل کردن UI به کد برنامه (Source Code)

المان هایی که در storyboard مشاهده می کنید، در واقع هر یک به بخشی از source code وصل است. شما باید این رابطه ی بین storyboard با کد برنامه را به خوبی درک کنید.  

در Storyboard، یک scene نشانگر صفحه ای از صفحات متعدد برنامه است که بخشی از محتوای برنامه و عموما یک view controller را شامل می شود. view controller ها رفتار (متدهای) اپلیکیشن شما را پیاده سازی می کنند.    

view controller تنها یکview  content و view های زیرمجموعه ی آن را اداره می کند (content view = view دربرگیرنده ی تمامی view ها یا آبجکت های یک scene همچون button، text field و label).

View controller ها: 1. تعاملات/گردش اطلاعات بین data model برنامه (بخشی که داده های اپلیکیشن را کپسوله سازی می کند) و view ها را (بخش هایی که داده های برنامه را برای کاربر به نمایش می گذارد) مدیریت می کند 2. چرخه ی حیات content view های برنامه (view ای که در بالای سلسله مراتب قرار گرفته و نقش ظرف را برای view های فرزند خود همچون button، label ایفا می کند) را از ابتدا تا انتها مدیریت می نماید 3. تغییرات در جهت نمایش برنامه را به هنگام چرخش دستگاه اداره می کند 4. قابلیت پیمایش در برنامه را تعریف کرده  5. و در پایان رفتاری که به ورودی کاربر واکنش نشان می دهد را پیاده سازی می نماید.

تمامی آّبجکت های view controller در IOS، از جنس کلاس UIViewController یا یکی از کلاس های فرزند آن هستند.

رفتار view controller ها را بایستی در کد با ایجاد و پیاده سازی subclass های اختصاصی از view controller (کلاس های فرزند view controller با پیاده سازی اختصاصی خود)، تعریف نمایید. سپس می توانید یک اتصال بین کلاس های مذکور و scene ها در storyboard ایجاد کرده و در نهایت برنامه ای داشته باشید که رفتار تعریف شده در کد (کلاس های view controller) را همراه با UI مورد نظر در storyboard، ارائه می دهد. 

Xcode قبلا چنین کلاسی را با نام ViewController.swift ایجاد کرده و آن را به scene جاری که هم اکنون در storyboard با آن کار می کنید، متصل کرده است. در آینده، همین که scene های بیشتری را به برنامه اضافه می کنید، این اتصال بین scene و view controller را خود از طریق Identity inspector برقرار می نمایید.

Identity inspector این امکان را به شما می دهد تا آن دسته از property های یک آبجکت را که با identity آن مرتبط هستند، مانند اینکه به کدام کلاس تعلق دارد، ویرایش نمایید.

clip_image004[6]

در زمان اجرای برنامه (runtime)، storyboard نمونه ای از کلاس ViewController می سازد (همان subclass اختصاصی view controller). صفحه ای از برنامه را که در storyboard می بینید، رابط کاربری و ظاهر خود را از scene جاری گرفته، و رفتارهایش، عملیاتی که می تواند انجام دهد، را از کدهای فایل ViewController.swift دریافت می کند.

اگرچه scene مورد نظر به کدهای فایل ViewController.swift متصل است، اما این اتصال به تنهایی برای داشتن تعامل بین بخش های مختلف برنامه کافی نیست. برای تعریف تعامل در برنامه ی خود، کد view controller بایستی بتواند با view های موجود در storyboard (آبجکت های text field، label) تبادل اطلاعات داشته باشد. این کار را با ایجاد connection های بیشتر – که outlet و action خوانده می شوند – بین view ها در storyboard و فایل های view controller انجام می دهید.

تعریف Outlet برای المان های UI

Outlet روشی تعریف می کند که می توان به وسیله ی آن به آبجکت های رابط کاربری – آبجکت هایی نظیر label، button که به storyboard اضافه شده – از کد فایل های source code اشاره کرد (دسترسی داشت).

برای ایجاد outlet، کافی است از آبجکت مورد نظر در storyboard، به فایلview controller ، control-drag کنید. بدین معنی که با کلیک بر روی آن آبجکت، نگه داشتن کلید control و کشیدن آن به فایل view controller، یک اشاره گر از آن آبجکت در فایل view controller ایجاد نمایید.

این عملیات یک (متغیر) property برای آبجکت مورد نظر در فایل view controller ایجاد می کند و به شما اجازه می دهد در زمان اجرا از کد به آن آبجکت دسترسی داشته و آن را دستکاری کنید.   

با توجه به آنچه گفته شد، جهت دسترسی یا اشاره به label و text field در لایه ی رابط کاربری، باید برای آن ها در کد outlet تعریف نمایید. 

به منظور وصل کردن المان text field به کد فایل ViewController.swift:

1.      فایل Main.storyboard را باز کنید.

2.      Assistant editor را با کلیک بر روی دکمه ی Assistant، واقع در نوار ابزار Xcode، بالای محیط گوشه ی سمت راست، باز نمایید.

clip_image006[6]

3.      اگر به فضای کاری بیشتری نیاز دارید، می توانید project navigator و utility area را با کلیک بر روی دکمه های مربوطه در نوار ابزار Xcode پنهان نمایید (جمع کنید).

clip_image008[6]

در صورت لزوم می توانید outline view را نیز جمع کنید.

4.      در editor selector bar، در بالای assistant editor، این مسیر را طی کنید: Preview -> Automatic -> viewController.swift.

clip_image010[6]

محتویات فایل ViewController.swift در ویرایشگر سمت راست محیط به نمایش در می آید.

1.      در فایل ViewController.swift، خطی که کلیدواژه ی class را دارد، پیدا کنید:

class ViewController: UIViewController {

2.      در زیر کد ذکر شده، عبارت ذیل را درج کنید:

// MARK: Properties

با این کار در واقع یک comment (کد درج توضیحات) به source code خود اضافه می کنید. همان طور که قبلا گفته شد، comment یک تکه متن در فایل source code است که به هنگام کامپایل به عنوان بخشی از دستورات برنامه ترجمه و اجرا نمی شود، اما اطلاعات کاربردی در خصوص بخش های مختلف کد ارائه می دهد.

Comment ای که با کاراکترهای // MARK: آغاز می شود، یک نوع خاص comment است که بیشتر به منظور سازمان دهی کد و کمک در فهم کاربرد آن بخش مورد استفاده قرار می گیرد.

به عنوان نمونه، تکه کدی که شما به عنوان comment به فایل نام برده اضافه کردید، اعلان می کند که در آن بخش property های آبجکت لیست می شوند.  

1.      در storyboard، المان text field را انتخاب کنید.

2.      بر روی المان مذکور در canvas کلیک کرده، کلید control را نگه دارید، سپس آن را کشیده و در زیر بخش comment در assistant editor جایگذاری نمایید.

clip_image012[6]

3.      یک پنجره ی محاوره ای به نمایش در می آید. در فیلد Name، واژه ی nameTextField را وارد کنید.  

لازم نیست دیگر تنظیمات را تغییر دهید.

 

clip_image014[6]

4.      حال بر روی دکمه ی Connect کلیک نمایید. Xcode کد لازم را به فایل ViewController.swift، جهت ذخیره ی اشاره گری (pointer) به text field اضافه کرده و storyboard را برای برقراری اتصال مورد نیاز بین المان UI و کد تنظیم می کند. به عبارت واضح تر، Xcode یک اشاره گر (در قالب متغیر یا property) به المان UI مورد نظر، در فایل ViewController.swift اضافه کرده و سپس storyboard را جهت برقراری ارتباط بین المان مورد نظر در لایه ی رابط کاربری با کد مربوطه ی آن در فایل مزبور تنظیم می کند.  

@IBOutlet weak var nameTextField: UITextField!

مدت زمانی را به فهم این خط کد اختصاص دهید.

خصیصه (attribute) IBOutlet این امکان را برای شما فراهم می کند تا در محیط Xcode از interface builder به متغیر (property) nameTextField وصل شوید (پیشوند IB نیز به همین امکان اشاره دارد). کلیدواژه ی weak بیانگر قابلیت nil بودن متغیر در طول عمر آن می باشد (بدین معنی که property مذکور می تواند در برهه ای از زمان هیچ مقداری نداشته باشد). در بقیه ی (تعریف) کد صرفا یک متغیر از نوع (کلاس) UITextField، به نام nameTextField ایجاد می شود.  

به علامت تعجب "!" در انتهای تعریف outlet دقت کنید. اگر بخاطر داشته باشید، برخی از property ها در فایل AppDelegate.swift این علامت را در انتهای خود دارند. علامت مزبور نشانگر این است که متغیر یا property مورد نظر از نوع implicitly unwrapped optional است. implicitly unwrapped optional، یک متغیر از جنس optional است که پس از مقداردهی اولیه همیشه حاوی مقدار خواهد بود (optional متغیری که می تواند مقداری داشته/نداشته باشد).

حال label را مانند المان قبلی (text field) به کد مربوطه ی آن در فایل ViewController.swift متصل کنید.  

برای وصل کردن المان label به کد مربوطه در فایل ViewController.swift:

1.      Label را در storyboard انتخاب نمایید.

2.      بر روی المان مذکور در canvas کلیک کرده و کلید Control را نگه دارید، سپس آن را کشیده و در assistant editor، زیر متغیر nameTextField جایگذاری کنید. 

clip_image016[6]

3.      در کادر محاوره ای که نمایان می شود، در فیلد Name، واژه ی mealNameLabel را به عنوان اسم المان وارد نمایید. لازم نیست تنظیمات دیگری انجام دهید.

clip_image018[6]

4.      بر روی Connect کلیک نمایید.

Xcode یک اشاره گر (pointer) در قالب متغیر  به المان Label در فایل ViewController.swift اضافه کرده، سپس storyboard را جهت برقراری ارتباط بین المان UI مورد نظر و کد مربوطه ی آن در فایل مذکور، تنظیم می کند. این outlet به استثنای دو ویژگی نوع و اسم، کاملا مشابه المان قبلی است (outlet جاری از نوع کلاس UILabel است که با نوع آبجکت در storyboard کاملا همخوانی دارد).  

1. @IBOutlet weak var mealNameLabel: UILabel!

حالا که روشی برای اشاره و دسترسی به المان های UI از داخل کد (در فایل ViewController.swift) دارید، باید یک رخداد که توسط کاربر فعال می شود و زمینه ی تعامل میان این دو المان را فراهم می کند، تعریف نمایید. برای تعریف این رخداد در IOS از action بهره می گیریم. 

 

تعریف یک رخداد با استفاده از Action

اپلیکیشن های IOS رخداد محور هستند (بر اساس الگوی برنامه نویسی رخداد محور/event-driven طراحی می شوند). بدین معنی که روند اجرای (flow) برنامه توسط event ها: رخدادهای سیستم و action های کاربر (کلیک موس یا فشردن کلیدهای کیبورد یا دکمه ای بر روی صفحه) تعیین می گردد.

 کاربر با UI برنامه تعامل برقرار کرده ، برای مثال یک دکمه را کلیک می کند، در پی این عمل/action کاربر رخداد خاصی اتفاق می افتد. این رخدادها خود سبب اجرای منطق برنامه و تغییر در داده های آن می شود. سپس واکنش برنامه به عمل کاربر و اثر آن، در UI برنامه منعکس می شود.

همان طور که می دانید در برنامه های رویداد محور، کنترل اجرای بخش هایی از برنامه به دست کاربر است. بدین معنی که زمانی که کاربر با UI برنامه تعامل می کند، بخش های مختلف از کد برنامه در پاسخ به عمل کاربر اجرا می شود. حال از آنجایی که کاربر، نه شما، کنترل اینکه چه زمانی بخش هایی از کد برنامه اجرا شوند را در دست دارد، شما به عنوان برنامه نویس بایستی مشخص کنید کاربر دقیقا اجازه ی انجام چه عملیاتی را دارد و در پاسخ به آن اعمال دقیقا چه واکنشی بایستی نشان داده شود (چه اتفاقی رخ دهد).

Action (یا action method) یک تکه کد است که به event یا رخدادی در برنامه وصل بوده و با اتفاق افتادن آن event فراخوانی و اجرا می شود. به عبارتی دیگر زمانی که آن event رخ می دهد، متعاقبا کد یا دستور مرتبط با آن اجرا می گردد.

می توانید با تعریف action، عملیات دلخواه را پیاده سازی کنید. حال این عملیات می تواند دستکاری داده های مشخص، بروز رسانی بخشی از UI و یا هر عملیات دیگری باشد. در واقع شما با استفاده از action جریان یا روند اجرای برنامه را در پاسخ به رخدادهای فعال شده توسط کاربر و سیستم تعیین می کنید.     

تعریف action و outlet به یک نحو صورت می گیرد: کافی است بر روی آبجکت مورد نظر کلیک کرده، کلید control را نگه دارید. سپس آن را کشیده و داخل فایل view controller جایگذاری نمایید. به دنبال آن یک متد در فایل view controller تعریف می شود که به مجرد تعامل کاربر با آبجکت متصل (به آن متد)، فراخوانده و اجرا می گردد.

 

کار را با ایجاد یک action ساده آغاز نمایید: زمانی که کاربر در UI برنامه، دکمه ی Set Default Label Text را فشار می دهد، شما باید label را طوری تنظیم کرده باشید که (به مجرد فشرده شدن دکمه) مقدار پیش فرض Default Text را نمایش دهد. (در بخش بعدی label را طوری تنظیم می کنیم که مقدار وارد شده در text field را عینا نمایش دهد.)

جهت ایجاد یک action برای بازگردانی مقدار label به Default Text در فایل ViewController.swift، مراحل زیر را گام به گام دنبال نمایید:

1.      در فایل ViewController.swift، به قبل از آخرین "}"، عبارت زیر را اضافه نمایید:

// MARK: Actions

comment فوق نشانگر این است که action های کد در این بخش قرار می گیرند.

2.      بر روی دکمه ی Set Default Label Text کلیک کرده، کلید control را نگه دارید. سپس المان مزبور را کشیده و داخل فایل ViewController.swift، در زیر comment درج شده (// MARK: Actions)، جایگذاری کنید.

clip_image020[6]

3.      در کادر محاوره ای که پدیدار می شود، در فیلد Connection گزینه ی Action را انتخاب نمایید.

4.      در فیلد Type، گزینه ی UIButton را انتخاب نمایید.

حتما متوجه شدید که مقدار فیلد Type به صورت پیش فرض بر روی AnyObject تنظیم می شود. در زبان Swift، AnyObject یک نوع است و برای توصیف آبجکتی که می تواند به هر کلاسی تعلق داشته باشد، بکار می رود. تنظیم نوع این action method بر روی مقدار UIButton، بیانگر این است که تنها آبجکت های از جنس کلاس UIButton می توانند به این متد یا action وصل شوند. کادر محاوره ای پس از وارد کردن مقادیر مورد نیاز در آن به صورت زیر خواهد بود:

clip_image022[6]

5.      اکنون بر روی دکمه ی Connect کلیک نمایید.

محیط Xcode خود تعریف و کد لازم برای action method را اضافه می کند.

@IBAction func setDefaultLabelText(sender: UIButton) {

}

در این کد، پارامتر sender به آبجکتی که action را صدا زده و اجرای آن را سبب می شود – در این سناریو همان آبجکت button – اشاره دارد. خصیصه (attribute) IBAction نشانگر این است که متد مورد نظر یک action بوده که می توان از storyboard در interface builder به آن وصل شد. بقیه ی کد صرفا یک متد متعارف به نام setDefaultLabelText(_:) را تعریف می کند.

در حال حاضر، تعریف متد هیچ پیاده سازی در بدنه ی خود ندارد. در زیر کدی به بدنه ی متد اضافه می کنیم که مقدار label را به یک مقدار پیش فرض برمی گرداند.

به منظور پیاده سازی action یا متدی که مقدار label را در فایل ViewController به Default Text برمی گرداند، مراحل زیر را طی نمایید:

1.      در فایل ViewController.swift، تعریف متد setDefaultLabelText را پیدا کنید. 

2.      داخل بدنه ی متد (بین {})، این خط کد را اضافه نمایید:

mealNameLabel.text = "Default Text"

همان طور که حدس می زنید، این کد مقدار text (که یکی از property های label است) را برابر Default Text قرار می دهد. اگر به کد دقت کنید، متوجه می شوید که نوع Default Text را صریحا مشخص نکردیم و نیازی هم به این کار نیست. چراکه زبان Swift قابلیتی به نام type inference (استنتاج نوع) دارد و با استفاده از آن می تواند درست حدس بزند که مقداری از نوع رشته را به آبجکتی از جنس کلاس NSString تخصیص می دهید.

هم اکنون کد action method شما باید به صورت زیر باشد:

@IBAction func setDefaultLabelText(sender: UIButton) {

mealNameLabel.text = "Default Text"

}

تست کنید: تغییراتی که تاکنون به کد اعمال کرده اید را با شبیه ساز (Simulator) تست نمایید. زمانی که بر روی دکمه ی Set Default Label Text کلیک می کنید، مقدار label بایستی از Meal Name (مقداری که در storyboard تنظیم کردید) به Default Text (مقداری که در بدنه ی متد یا action تعریف کردید) تغییر کند.

clip_image024[6]

رفتاری که با این مثال پیاده سازی کردید، مصداق و نمونه ای از پیاده سازی الگوی توسعه ی target-action در طراحی برنامه های IOS است. target-action یک الگوی توسعه یا design pattern است که در آن آبجکت مورد نظر، با اتفاق افتادن یا فعال شدن رخدادی معین، message یا پیغامی را به آبجکت دیگری ارسال می کند. در این سناریو، event کلیک کاربر بر روی دکمه ی Set Default Label Text است، action همان متد setDefaultLabelText است، target همان ViewController، جایی که متد در آن تعریف شده است و در پایان sender (ارسال کننده ی پیغام) دکمه ی Set Default Label Text است.

Message در واقع یک action method است که در source code تعریف شده و target –دریافت کننده ی پیغام – آن آبجکتی است که قادر به اجرای action یا عملیات تعریف شده توسط متد می باشد. آبجکتی که پیغام را ارسال می کند معمولا یک کنترل همچون button، slider یا switch است که در پاسخ به عمل کاربر (لمس صفحه ی نمایش، کشیدن یا تغییر مقدار) رخدادی را فعال می کند (به ورودی کاربر واکنش نشان داده و رخدادی را اجرا می کند).

همان طور که گفته شد این الگو به وفور در برنامه نویسی IOS پیاده سازی شده و شما نمونه های بیشتری از آن را در مباحث بعدی این سری آموزشی مشاهده خواهید کرد.   

پردازش ورودی کاربر (برابر قرار دادن مقدار label با مقدار وارد شده توسط کاربر در text field)

در حال حاضر، برنامه طوری تنظیم است که با کلیک بر روی دکمه، مقدار label را بر روی Default Text قرار می دهد. اکنون می خواهیم رفتاری برای برنامه تعریف کنیم که با کلیک بر روی دکمه ی آن، مقدار label را برابر مقدار وارد شده توسط کاربر در text field قرار دهد. 

در این بخش جهت ساده نگه داشتن برنامه، برای اعلان زمان بروز رسانی مقدار label، متکی به action کاربر که همان کلیک بر روی دکمه ی Return صفحه کلید است، خواهید بود.

برای پردازش و کار با ورودی کاربر از آبجکت text field، به یک delegate از آبجکت مزبور نیاز دارید. Delegate یک آبجکت است که به نمایندگی از یا با همکاری آبجکت دیگری کاری را انجام می دهد. آبجکتی که کاری را به آبجکت دیگر محول می کند (به آن اشاره می کند) یا با هماهنگی آن آبجکت کاری را انجام می دهد در اینجا delegating object نامیده می شود. آن آبجکتی که کاری از جانب delegating object به آن سپرده می شود، delegate خوانده می شود.

Delegating object (در این مثالtext field )، یک اشاره گر (reference) از آبجکت دیگر (delegate) نزد خود نگه داشته و در زمان مناسب، delegating object نام برده یک پیغام به delegate ارسال می کند. حال این پیغام به delegate درباره ی رخدادی خبر می دهد که delegating object در صدد مدیریت آن بوده و یا به تازگی مدیریت کرده است. در پی آن، delegate با بروز رسانی ظاهر/وضعیت خود، آبجکت دیگر در برنامه یا بازگردانی مقداری که تعیین می کند یک رخداد در حال وقوع چگونه اداره شود، به این دست رخدادها واکنش نشان می دهد.  

delegate این text field، زمانی که مقدار کنترل مورد نظر (text field) در حال ویرایش است، با آن تعامل داشته و کاملا اطلاع دارد چه زمانی رخدادهای مهم اتفاق می افتد – مانند اینکه کاربر چه زمانی ویرایش متن را آغاز و کی آن را متوقف می کند. delegate می تواند با استفاده از این اطلاعات، داده ها را در زمان مناسب ذخیره/حذف کند، صفحه کلید را ببندد و غیره ... .   

هر آبجکتی می تواند به عنوان delegate آبجکتی دیگری ایفای نقش کند مادام اینکه از الگو یا protocol مناسب پیروی نماید. Protocol یا الگویی که delegate کنترل field text را تعریف می کند، UITextFieldDelegate نام دارد (protocol = صرفا یک الگو و معرفی یک سری متد و property است. اگر برای آبجکت یا کلاسی protocol اعلان کنید، آن کلاس یا آبجکت ملزم به پیاده سازی متدها و property های تعیین شده توسط آن protocol می شود). در این مثال، از آنجایی که ViewController اشاره گر به آبجکت text field را نگهداری می کند، view controller را delegate آن text field انتخاب می کنیم.

ابتدا بایستی ViewController را بر اساس protocol یا الگوی UITextFieldDelegate تنظیم و پیاده سازی کنید. برای استفاده از protocol خاص، کافی است آن را در خط تعریف کلاس لحاظ نمایید.

به منظور پیروی از الگوی پیاده سازی UITextFieldDelegate در view controller و انتساب کلاس نام برده به عنوان دلیگیت text field، مراحل زیر را گام به گام دنبال نمایید: 

1.      در صورتی که assistant editor باز است، با کلیک بر روی آخرین دکمه، از سمت چپ، standard editor را باز کنید.

clip_image026[6]

Project navigator و utility area را با کلیک به ترتیب بر روی دکمه های Navigator و Utilities در نوار ابزار محیط Xcode (اشاره شده در تصویر فوق) باز نمایید.

2.      در project navigator، بر روی فایل ViewController.swift کلیک کرده و آن را باز نمایید. 

3.      داخل فایل مزبور، خط حاوی کلیدواژه ی class را پیدا کنید:

class ViewController: UIViewController {

4.      پس از UIViewController، یک ویرگول اضافه نموده و سپس UITextFieldDelegate را درج نمایید. با این کار شما protocol نام برده را adopt می کنید یا به عبارتی الگویی برای معرفی آن کلاس به عنوان delegate ارائه می دهید (protocol = صرفا یک الگو و معرفی یک سری متد و property است. اگر برای آبجکت یا کلاسی protocol اعلان کنید، آن کلاس یا آبجکت ملزم به پیاده سازی متدها و property های تعیین شده توسط protocol می شود).

class ViewController: UIViewController, UITextFieldDelegate {

با اضافه نمودن این protocol به خط تعریف کلاس ViewController، به کلاس ذکر شده این قابلیت را دادید که خود را به عنوان UITextFieldDelegate معرفی کند. به عبارت دیگر شما می توانید از این پس، از کلاس نام برده به مثابه ی delegate آبجکت text field استفاده کرده، برخی از رفتارهای (متدهای) آن را پیاده سازی و بدین وسیله ورودی کاربر را مدیریت نمایید.

به منظور انتساب کلاس ViewController به عنوان delegate آبجکت nameTextField، مراحل زیر را دنبال نمایید:

1.      داخل فایل ViewController.swift ،متد viewDidLoad() را پیدا کنید:

override func viewDidLoad() {

super.viewDidLoad()

// Do any additional setup after loading the view, typically from a nib.

}

در پیاده سازی این متد یک comment مشاهده می کنید. نیازی به این comment نیست، می توانید آن را حذف کنید.

2.      در زیر دستور super.viewDidLoad()، ابتدا یک خط خالی ایجاد کرده، سپس کد زیر را وارد نمایید:

// Handle the text field’s user input through delegate callbacks.

nameTextField.delegate = self

کلیدواژه ی self به کلاس ViewController اشاره دارد، زیرا آبجکت text field در محدوده یا scope تعریف کلاس ViewController مورد اشاره قرار گرفته است.

شما می توانید برای فهم کاربرد و استفاده ی هر بخش کد، توضیحات و comment در زیر آن درج نمایید. 

پس از اعمال تغییرات فوق، کد شما باید مشابه زیر باشد:

override func viewDidLoad() {

super.viewDidLoad()

// Handle the text field’s user input through delegate callbacks.

در این بخش از کد ورودی کاربر در تکست فیلد را از طریق توابع بازفراخوان یا کالبک دلیگیت مدیریت می کنیم//

nameTextField.delegate = self

}

اکنون کلاس ViewController یک delegate برای آبجکت nameTextField است.

UITextFieldDelegate تعدادی متد اختیاری نیز ارائه می دهد که شما ملزوم به پیاده سازی آن ها نیستید. اما برای اینکه برنامه جاری رفتار و عملیات دلخواه را داشته باشد، بایستی دو متد از مجموع متدهای آن را پیاده سازی کنید:

func textFieldShouldReturn(textField: UITextField) -> Bool

func textFieldDidEndEditing(textField: UITextField)

برای درک اینکه چه زمان این متدها فراخوانده شده و چه کارهایی را انجام می دهند، آگاهی از نحوه ی عملکرد و واکنش text field ها به ورودی کاربر لازم و ضروری است.

زمانی که کاربر text field را با قابلیت لمس انتخاب می کند، کنترل نام برده (text field) به صورت خودکار first responder می شود. در یک برنامه، first responder اولین آبجکتی است که انواع event های برنامه از جمله key event ها (رخدادهای مربوط به دکمه)، motion event (رخدادهای مربوط به حرکت یا تکان خوردن دستگاه)، action message ها و غیره ... را دریافت می کند. به عبارت دیگر، بسیاری از رخدادهای فعال شده بر اثر اَعمال کاربر ابتدا به first responder ارجاع داده می شوند.   

از آنجایی که text field به صورت پیش فرض اولین المان واکنش دهنده یا first responder می باشد، سیستم عامل IOS صفحه کلید را به نمایش گذاشته و session ویژه ی درج و ویرایش مقدار آن المان را مهیا و راه اندازی می کند. آنچه کاربر با فشردن دکمه های صفحه کلید تایپ می کند، بلادرنگ وارد المان text field می شود.

زمانی که کاربر قصد دارد ویرایش مقدار text field را متوقف کند، text field بایستی از موقعیت خود به عنوان first responder دست بکشد (از این وضعیت خارج شود). به دنبال این اتفاق، چون که المان text field دیگر آبجکت فعال در برنامه نیست، رخدادهای بعدی می بایست به آبجکت دیگری ارجاع یا سوق داده شوند.

در اینجا است که پیاده سازی متدهای UITextFieldDelegate بکار گرفته و اجرا می شود. شما می بایست اعلان کنید که با اتمام editing session و کلیک کاربر بر روی دکمه ی مربوطه (Return یا Done)، المان text field بایستی از موقعیت خود به عنوان first responder  دست بکشد (جایگاه خود به عنوان آبجکت فعال در پنجره و دریافت کننده ی تمامی رخدادها رها کند). این کار را در بدنه ی متد textFieldShouldReturn(_:) تعریف و پیاده سازی می کنید. متد مذکور زمانی که کاربر بر روی دکمه ی Return (یا در مثال جاری دکمه ی Done) کلیک می کند، فراخوانده و متعاقبا اجرا می گردد.  

جهت پیاده سازی متد textFieldShouldReturn(_:) از protocol یا الگوی UITextFieldDelegate، مراحل زیر را دنبال نمایید:

1.      داخل فایل ViewController.swift، در بالای بخش // MARK: Actions، کد زیر را وارد نمایید:

// MARK: UITextFieldDelegate

این comment یا کد درج توضیحات به سازمان دهی کد، خوانایی بیشتر و فهم کاربرد بخش های مختلف آن کمک شایانی می کند. 

تاکنون تعداد زیادی از این comment ها را به کد خود اضافه کردید. Xcode هر یک از این comment ها را به صورت یک section title (متن یا عنوان توصیف کننده ی بخشی از سند یا صفحه) در functions menu فایل کد اصلی برنامه (source code) لیست می کند. برای دسترسی به functions menu، کافی است بر روی اسم فایل مربوطه در بالای ناحیه ی ویرایش (editor area) کلیک نمایید. functions menu به شما امکان می دهد تا با کلیک بر روی آیتم های قابل گزینش آن، به بخش های مرتبط در کد خود پیمایش کنید (بپرید). با کمی دقت می بینید که کلیه ی بخش هایی که قبلا با // MARK: علامت گذاری کردید، در این بخش قابل مشاهده می باشد. می توانید بر روی یکی از section title ها کلیک کرده تا به سرعت به بخش مربوطه در فایل هدایت شوید.     

2.      clip_image028[6]در زیر comment، متد زیر را درج نمایید:

func textFieldShouldReturn(textField: UITextField) -> Bool {

}

3.      در بدنه ی این متد، کد زیر را وارد نموده تا text field از موقعیت خود به عنوان first responder دست بکشد. همچنین یک comment جهت ارائه ی توضیحات در خصوص کاربرد کد، در بالای آن درج نمایید.

// Hide the keyboard.

textField.resignFirstResponder()

4.      سعی کنید خط دوم کد را (متد textField.resignFirstResponder()) را (بجای کپی و پیست) به صورت دستی تایپ کنید. در حین کدنویسی متوجه خواهید شد که Xcode سعی می کند بر اساس شرایط و بستر جاری، کدی که قصد وارد کردن آن را دارید، حدس زده و بخش های بعدی آن را به شما پیشنهاد کند. این ویژگی که code completion نامیده می شود، تنها یکی از امکانات Xcode است که در افزایش سرعت برنامه نویسی می تواند کمک شایانی به شما بکند.  زمانی که Xcode لیست پیشنهادات یا کدهای تکمیل کننده ی خود را به صورت لیست ارائه می دهد، کافی است با نوار پیمایش آن به پایین لیست رفته و پس از یافتن مورد مناسب، کلید Return را فشار دهید. خواهید دید که Xcode کل خط را به صورت خودکار برای شما وارد می کند. 

clip_image030[6]

5.      در بدنه ی متد جاری، دستور زیر را نیز وارد نمایید:

1. return true

از آنجایی که این متد یک مقدار بولی (Boolean) در خروجی برمی گرداند، بازگردانی مقدار true توسط متد مورد نظر نشانگر این است که text field بایستی در پاسخ به اِعمال کلید Return (توسط کاربر) صفحه کلید را ببندد (از موقعیت خود به عنوان first-responder یا آبجکت فعال در پنجره دست بکشد).

متد textFieldShouldReturn(_:) هم اکنون می بایست مشابه زیر باشد:

func textFieldShouldReturn(textField: UITextField) -> Bool {

// Hide the keyboard.

textField.resignFirstResponder()

return true

}

دومین متدی که باید پیاده سازی کنید، textFieldDidEndEditing(_:)، زمانی فراخوانده می شود که text field از موقعیت خود به عنوان اولین آبجکت واکنش دهنده (first-responder) دست بکشد (از این وضعیت خارج شود). این متد پس از اجرای textFieldShouldReturn (متدی که در بالا پیاده سازی کردید)، صدا خورده می شود.

تابع textFieldShouldReturn به شما این امکان را می دهد تا اطلاعات وارد شده در text field را خوانده و آن را برای منظور خاصی بکار ببرید. برای مثال، در شرایط فعلی شما مقدار وارد شده در text field را گرفته و از آن برای تغییر مقدار label در رابط کاربری برنامه ی خود استفاده می کنید.

جهت پیاده سازی متد textFieldDidEndEditing(_:) از protocol یا الگوی UITextFieldDelegate، کافی است مراحل زیر را گام به گام دنبال نمایید:

1.      داخل فایل ViewController.swift، پس از متد textFieldShouldReturn(_:)، متد زیر را درج نمایید:

func textFieldDidEndEditing(textField: UITextField) {

}

2.      در بدنه ی این متد، دستور زیر را وارد نمایید:

1. mealNameLabel.text = textField.text

با افزودن این متد خواهید توانست مقدار وارد شده در text field را جایگزین مقدار label (در بالای کادر متن) کرده و این تغییر را در UI معنکس و برای کاربر نمایش دهید.

در حال حاضر کد متد textFieldDidEndEditing(_:) شما باید به صورت زیر باشد:

func textFieldDidEndEditing(textField: UITextField) {

mealNameLabel.text = textField.text

}

تست کنید: برنامه ی خود را به همراه تمامی تغییراتی که اعمال کرده اید، در محیط شبیه ساز/ Simulator اجرا و تست کنید. در برنامه باید بتوانید text field را انتخاب کرده و مقداری از نوع متن را در آن وارد کنید. زمانی که بر روی دکمه ی Done کلیک می کنید، صفحه کلید باید بسته شده و متن label می بایست مقدار وارد شده در text field را عینا نمایش دهد. حال اگر بر روی دکمه ی Set Default Label Text کلیک نمایید، مقدار label بایستی تغییر کرده و به مقدار اولیه Default Text (مقداری که قبلا توسط action تعیین کرده بودید) بازگردد .   

clip_image031[6]

fghfghgf6551fh05g101hgfh

ساخت یک رابط کاربری/UI ساده

این مبحث شما را با Xcode، محیط برنامه نویسی و ساخت اپلیکیشن برای سیستم عامل IOS، آشنا می سازد. در این مبحث همچنین با ساختار پروژه در Xcode آشنا شده و نحوه ی پیمایش بین کامپوننت ها و استفاده از آن ها را خواهید آموخت.

از ابتدا تا انتهای مبحث، یک رابط کاربری ساده (UI) برای اپلیکیشن FoodTracker طراحی کرده و سپس آن را در محیط شبیه ساز (Simulator) اجرا و مشاهده می کنید. در پایان برنامه ظاهری مشابه زیر خواهد داشت:

clip_image002

آنچه خواهید آموخت

· یک پروژه جدید در محیط کاری Xcode ایجاد نمایید.

· کاربرد فایل های اصلی که به همراه الگو یا قالب پروژه (project template) ایجاد می شوند را بدانید.

· فایل های پروژه را باز کرده و بین آن ها راه گزینی (switch) کنید.

· یک اپلیکیشن را در محیط شبیه ساز (simulator) اجرا نمایید.

· در سطح storyboard المان های UI جدید اضافه کرده، آن ها را جابجا و تغییر اندازه دهید.

· با استفاده از Attribute inspector، attribute های المان های UI را در storyboard ویرایش نمایید.

· المان های UI را با استفاده از outline view مشاهده و در صورت لزوم مجددا سازمان دهی نمایید.

· با استفاده از Preview assistant editor، پیش نمایشی از یک UI storyboard (پیش نمایشی از طرح کلی و ظاهر برنامه) داشته باشید.

· UI طراحی کنید که به وسیله ی امکان Auto Layout خود را با اندازه ی دستگاه میزبان (کاربر) تنظیم می کند.

ایجاد پروژه ی جدید

Xcode الگو یا template های درون ساخته ی متعددی دارد که ساخت و توسعه ی اپلیکیشن های متعارف IOS همچون بازی ها، برنامه هایی با قابلیت پیمایش با تب (tab-based navigation) و همچنین اپلیکیشن های با نمای جدولی (table-view-based) را آسان می سازد. بیشتر این template ها دارای interface و فایل های source code از پیش تنظیم شده هستند و شما می توانید از آن ها به صورت آماده استفاده کنید.

در آموزش حاضر کار را با ساده ترین نوع template آغاز می کنیم: Single View Application.

برای ایجاد پروژه ی جدید:

1. Xcode را از پوشه ی /Applications  باز کنید. پنجره ی زیر به نمایش در می آید.

clip_image004

ممکن است بجای پنجره ی فوق، پنجره ی پروژه نمایش داده شود. جای نگرانی نیست، احتمالا از قبل یک پروژه در محیط Xcode تعریف کرده یا فایل یک پروژه ی آماده را باز کرده اید. کافی است بر روی آیتم مربوطه در منو برای ایجاد پروژه ی جدید کلیک نمایید.

2. در پنجره ی آغازین (welcome window)، بر روی گزینه ی “Create a new Xcode project” کلیک کرده و یا این گام ها را دنبال نمایید: File > New > Project.

Xcode یک پنجره ی جدید باز کرده و پنجره ی محاوره ای را نمایش می دهد که می توان در آن template یا الگوی پروژه را انتخاب نمود.

3. در کادر کناری سمت چپ پنجره، گزینه ی Application را از بخش مربوط به IOS انتخاب نمایید.

4. داخل ناحیه ی اصلی پنجره ی محاوره ای، آیکون Single View Application را انتخاب کرده و سپس دکمه ی Next را کلیک نمایید.

image: ../Art/2_singleviewapp_template_2x.png

5. پنجره ی محاوره ای دیگر پدیدار می شود. مقادیر مشخص شده در زیر را وارد فیلدهای آن نموده و تنظیمات لازم را انجام دهید.

· Product Name: FoodTracker

همان طور که از اسم آن پیدا است، Xcode از این فیلد برای نام گذاری پروژه و اپلیکیشن شما بهره می گیرد.

· Organization Name: اسم شرکت تولید نرم افزار یا اسم خودتان را در این فیلد وارد نمایید. می توانید فیلد نام برده را خالی بگذارید.

· Organization Identifier: شناسه ی شرکت خود را وارد نمایید. در اینجا com.example را به عنوان مثال وارد می کنیم.

· Language: Swift

· Devices: Universal

اپلیکیشن Universal قابلیت اجرا بر روی iPhone و iPad را دارا می باشد.

· Bundle identifier: این مقدار به صورت خودکار بر اساس organization identifier و product name تولید می شود.

· Use Core Data: Unselected.

· Include Unit Tests: Selected

· Include UI Tests: Unselected.

image: ../Art/2_newproject_2x.png

6. بر روی دکمه ی Next کلیک کنید.

7. در پنجره ی محاوره ای که به نمایش در می آید، آدرس یا محل ذخیره فایل های پروژه ی خود را انتخاب کرده و بر روی Create کلیک نمایید. Xcode، پروژه ی جدید را داخل workspace window باز می کند (work space = پنجره ی داخل محیط کاری Xcode که امکان مدیریت فایل ها و منابع پروژه و پیمایش در آن ها را برای شما فراهم می کند).

image: ../Art/2_workspacewindow_2x.png

در پنجره ی Workspace، ممکن است پیغام هشدار “No code signing identities found.” را مشاهده نمایید. پیغام ذکر شده بیانگر این است که محیط Xcode را برای توسعه اپلیکیشن و برنامه نویسی IOS تنظیم نکرده اید. اما جای هیچ نگرانی نیست، شما می توانید بدون انجام این تنظیمات، پروژه را کامل کنید.

آشنایی بیشتر با محیط کاری Xcode

هر آنچه برای ساخت یک اپلیکیشن کارامد به آن نیاز دارید را در محیط Xcode خواهید یافت. این محیط نه تنها فایل های تشکیل دهنده ی پروژه ی را سازمان دهی می کند، ابزارهایی جهت ویرایش کد و المان های UI فراهم نموده و به شما امکان می دهد اپلیکیشن خود را کامپایل و اجرا نمایید. جهت خطایابی و اشکال زدایی، Xcode یک debugger درون ساخته در اختیار شما قرار می دهد که با استفاده از آن می توانید خط به خط برنامه را بررسی و debug کنید.

توصیه می کنیم زمانی را به آشنایی با بخش های مختلف پنجره ی Xcode تخصیص دهید. چرا که بخش های نام گذاری شده در پنجره ی زیر را در سرتاسر پروژه های این سری آموزشی بکار خواهید برد. در نگاه اول ممکن است کمی گیج کننده به نظر برسد، اما لازم نیست نگران باشید. به هنگام استفاده، هر بخش با جزئیات بیشتری شرح داده خواهد شد.

image: ../Art/2_workspacewindow_callouts_2x.png

راه اندازی محیط شبیه ساز (Simulator)

از آنجایی که پروژه ی خود را با استفاده از یک template در Xcode ایجاد کردید (پروژه ی خود را بر مبنای template آماده پی ریزی کرده اید)، محیط اولیه ی اپلیکیشن به صورت خودکار برای شما تنظیم می شود. با اینکه هیچ کدی برای برنامه خود ننوشته اید، می توانید قالب آماده ی Single View Application را بدون هیچگونه تنظیمات اضافی کامپایل (build) و اجرا نمایید.

جهت کامپایل و اجرای اپلیکیشن، می توانید از اپلیکیشن Simulator (یا برنامه ی شبیه ساز IOS) که همراه با محیط Xcode ارائه می شود، استفاده نمایید. Simulator به شما امکان می دهد اپلیکیشن خود را (در محیط شبیه ساز) اجرا کرده و پیش نمایشی از عملکرد و قابلیت های آن در یک دستگاه واقعی IOS را مشاهده نمایید.

Simulator قادر است سخت افزار دستگاه های iPad و iPhone (با صفحه نمایش بزرگ و کوچک) را شبیه سازی کند. این امر به شما امکان می دهد برنامه ی کاربردی خود را بر روی دستگاهی که می خواهید اپلیکیشن بر روی آن اجرا شود (اپلیکیشن را ویژه ی آن طراحی و تولید می کنید)، تست کرده و از عملکرد صحیح آن در دستگاه میزبان اطمینان حاصل نمایید. برای این پروژه simulator را بر روی گزینه ی iPhone 6 تنظیم نمایید.

جهت اجرای برنامه در محیط شبیه ساز:

1. به نوار ابزار Xcode مراجعه نموده، و از منوی pop-up (که Scheme نام دارد) گزینه ی iPhone 6 را انتخاب نمایید. این منوی pop-up به شما اجازه می دهد تا شبیه ساز یا دستگاهی که اپلیکیشن بر روی آن اجرا می شود را انتخاب نمایید.

به خاطر داشته باشید که محیط میزبان برنامه را حتما بر روی گزینه ی iPhone 6 Simulator تنظیم نمایید، نه IOS Device.

image: ../Art/2_schememenu_2x.png

2. جهت اجرا، بر روی دکمه ی Run در نوار ابزار محیط Xcode (واقع در بالای محیط سمت چپ) کلیک نمایید.

image: ../Art/2_toolbar_2x.png

برای اجرای پروژه همچنین می توانید به دو روش رو به رو اقدام نمایید: 1. Product > Run 2. کلید Command-R را فشار دهید.

زمانی که برای اولین بار اپلیکیشینی را بر روی محیط Mac اجرا می کنید، Mac از شما می پرسد آیا مایلید developer mode را فعال نمایید یا خیر. این حالت به Xcode اجازه می دهد به برخی امکانات ویژه ی عیب یابی و debugging دسترسی داشته باشد، بدون اینکه برای هر بار دسترسی شما مجبور شوید گذرواژه ی لازم را وارد نمایید (برای دسترسی به برخی قابلیت های debugging و فایل های playground شما ملزوم به ارائه ی گذرواژه ی خود هستید. با فعال کردن این مد دیگر برای دسترسی به امکانات نام برده نیازی به ارائه ی پسورد نیست). پس از تصمیم گیری در خصوص فعال کردن/نکردن developer mode، بر روی دکمه ی مربوطه کلیک نمایید.

image: ../Art/2_developermode_2x.png

برای پروژه های این سری آموزشی، developer mode را فعال نمایید.

4. همین که فرایند build تکمیل می شود، به نوار ابزار Xcode دقت کنید. Xcode اطلاعاتی را در خصوص فرایند build، داخل activity viewer نمایش می دهد. activity viewer در وسط نوار ابزار محیط Xcode قرار دارد.

پس از اینکه Xcode پروژه ی شما را با موفقیت کامپایل کرد، شبیه ساز به صورت خودکار راه اندازی می شود. راه اندازی برای اولین بار ممکن است کمی زمان ببرد.

شبیه ساز/simulator در حالت iPhone mode اجرا شده، سپس اپلیکیشن شما در صفحه نمایش شبیه ساز اجرا و به نمایش گذاشته می شود. اما قبل از اینکه شبیه ساز اپلیکیشن را به طور کامل راه اندازی (launch) کند، launch screen برنامه به همراه اسم آن (FoodTracker) به مدت چند ثانیه نشان داده شده و سپس خود اپلیکیشن اجرا می شود.

image: ../Art/2_sim_launchscreen_2x.png

سپس صفحه ی زیر به نمایش در می آید:

image: ../Art/2_sim_blank_2x.png

در حال حاضر، قالب Single View Application کار خاصی انجام نمی دهد – همان طور که می بینید صرفا یک صفحه ی سفید را برای کاربر به نمایش می گذارد. سایر Template هایی که Xcode در اختیار شما قرار می دهد، از قابلیت ها و رفتارهای بسیار پیچیده تری برخوردار هستند.

لازم است قبل از انتخاب یک template و توسعه ی آن برای ساخت اپلیکیشن خود، از موارد استفاده و قابلیت های آن کاملا مطلع باشید. اجرای برنامه در محیط شبیه ساز، بدون ایجاد تغییرات در آن روشی خوبی برای فهم این مسئله هست.

از محیط شبیه ساز خارج شوید (Simulator > Quit Simulator یا کلید Command-Q را فشار دهید).

مشاهده و بررسی Source code پروژه

به هنگام انتخاب قالب Single View Application، تعدادی فایل source code همراه آن ارائه می شود که این فایل ها وظیفه ی تنظیم محیط اپلیکیشن را بر عهده دارند. ابتدا فایل AppDelegate.swift  را مورد بررسی قرار می دهیم.

برای مشاهده ی محتویات فایل AppDelegate.swift:

1. ابتدا project navigator را در navigator area باز کنید. project navigator تمامی فایل های پروژه را نمایش می دهد. اگر project navigator باز نبود، می توانید آن را با کلیک بر روی دکمه ی سمت چپ در نوار navigator selector، باز نمایید (یا می توانید این مسیر را طی نمایید: View > Navigators > Show Project Navigator).

image: ../Art/2_projectnavigator_2x.png

2. در صورت لزوم، پوشه ی FoodTracker را (در کادر project navigator) با کلیک بر روی آیکون مثلث کوچک در کنار آن، باز نمایید.

3. فایل AppDelegate.swift را انتخاب نمایید. Xcode فایل نام برده را داخل editor area (ناحیه ی ویرایش) پنجره ی محیط باز کرده و محتویات/کدهای آن را به نمایش می گذارد.

image: ../Art/2_appdelegate_file_2x.png

و یا جهت مشاهده ی محتویات فایل در پنجره ی مجزا، دوبار بر روی فایل AppDelegate.swift کلیک نمایید.

فایل App Delegate

فایل AppDelegate.swift دو کاربرد اصلی دارد:

· علاوه برentry point یا نقطه ی شروع اجرای برنامه، یک run loop تعریف می کند که وظیفه ی تحویل رخدادهای ورودی به برنامه را دارد (run loop یک حلقه ی پردازش event است که برای زمان بندی کارها و هماهنگ سازی دریافت رخدادهای ورودی به برنامه، مورد استفاده قرار می گیرد. به عبارت دیگر هدف از run loop، فعال نگه داشتن thread به هنگام نیاز/زمانی که عملیاتی و کارهایی باید پردازش و انجام شوند و در حالت sleep قرار دادن آن در زمان بی کاری است). این کار توسط attribute ای به نام @UIApplicationMain، درج شده در بالای فایل مزبور، انجام می شود.

UIApplicationMain در واقع یک application object ( مسئول مدیریت life cycle برنامه از زمان اجرا تا پایان) و یک آبجکت app delegate تعریف می کند. در زیر به شرح مفهوم app delegate خواهیم پرداخت.

· این فایل همچنین کلاس AppleDelegate را تعریف می کند. کلاس مذکور الگو یا نقشه ای برای (ساخت) آبجکت app delegate می باشد. app delegate نیز پنجره ای را می سازد که محتوای اپلیکیشن شما در آن به تصویر کشیده شده و بستری را تعریف می کند که در آن می توان به تغییرات در وضعیت برنامه واکنش نشان داد. کدهای اختصاصی در سطح اپلیکیشن (custom app-level code­­­­­­­) داخل کلاس AppDelegate تعریف می شوند.

کلاس AppleDelegate یک property بیشتر ندارد و آن window است. app delegate به کمک این property پنجره ای که محتوای برنامه در آن نمایش داده می شود را رصد می کند. window از نوع optional است بدین معنی که می تواند در برهه ای از زمان هیچ مقداری نداشته و به اصطلاح nil باشد.

var window: UIWindow?

کلاس AppleDelegate همچنین الگویی آماده برای پیاده سازی متدهای پرکاربرد و مهم ارائه می دهد. این متدهای از پیش تعریف شده به application object امکان می دهند تا با app delegate تعامل داشته و ارتباط برقرار کند.

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool

func applicationWillResignActive(application: UIApplication)

func applicationDidEnterBackground(application: UIApplication)

func applicationWillEnterForeground(application: UIApplication)

func applicationDidBecomeActive(application: UIApplication)

func applicationWillTerminate(application: UIApplication)

در طول تغییر برنامه از یک وضعیت به وضعیت دیگر (state transition) – برای مثال، اجرا و شروع برنامه، تغییر وضعیت به پس زمینه و اتمام آن – application object متد متناظر در app delegate را صدا زده و به آن امکان می دهد پاسخ یا واکنش مناسب را نشان دهد.

لازم نیست زمان فراخوانی این متدها را خود مدیریت کرده و از اجرای به موقع آن ها اطمینان حاصل کنید. application object این وظیفه را بر عهده می گیرد.

هر یک از این متدها (که به صورت خودکار پیاده سازی می شوند) رفتار پیش فرضی داشته و عملیات خاصی را انجام می دهند. اگر پیاده سازی های الگو (template implementation) را خالی گذاشته یا آن را کلا از کلاس AppDelegate حذف کنید، در آن صورت هرگاه که متد صدا خورده می شود، دقیقا همان رفتار پیش فرض را از خود نشان خواهد داد. می توانید به این method template ها (الگو پیاده سازی یا قالب آماده ی متدها) کد اختصاصی خود را اضافه کنید تا با فراخوانی آن متد، رفتار دلخواه را به اجرا بگذارند.

در این مبحث هیچ کد اختصاصی به فایل AppDelegate.swift اضافه نخواهیم کرد.

فایل View Controller

با انتخاب قالب Single View Application، یک فایل دیگر به نام ViewController.swift ایجاد می شود. برای مشاهده ی محتوای این فایل بر روی ViewController.swift  در کادر project navigator کلیک نمایید.

image: ../Art/2_viewcontroller_file_2x.png

این فایل یک کلاس فرزند/subclass از UIViewController به نام ViewController ایجاد می کند. در حال حاضر، این کلاس صرفا رفتارهای (تعریف شده توسط) کلاس UIViewController را به ارث می برد. جهت بازنویسی (override) یا بسط این رفتارها، لازم است متدهای تعریف شده در UIViewController را override نمایید (همان طور که دو متد viewDidLoad() و didReceiveMemoryWarning() در فایل ViewController.swift  بازنویسی شده اند) و یا متدهای اختصاصی خود را پیاده سازی کنید.

در این فایل متدی به نامdidReceiveMemoryWarning()  نیز پیاده سازی شده که در حال حاضر با آن کاری نداریم. می توانید این متد را از کلاس ViewController حذف کنید.

پس از حذف متد ذکر شده، کد فایل ViewController.swift مشابه زیر خواهد بود:

import UIKit

class ViewController: UIViewController {

override func viewDidLoad() {

super.viewDidLoad()

// Do any additional setup after loading the view, typically from a nib.

}

}

در ادامه ی مبحث حاضر کدهایی را به این فایل اضافه خواهیم کرد.

مشاهده ی محتوای فایل Storyboard

در این بخش با فایل storyboard اپلیکیشن خود کار خواهیم کرد.

Storyboard در واقع فایلی است که نمودی (ظاهری) از (کدهای پشت) رابط کاربری اپلیکیشن را ارائه می دهد. به عبارتی روشن تر کدهای نوشته شده در فایل، به صورت مستقیم اجرا شده و می توان در هر لحظه خروجی کدها را مشاهده نمود.

در واقع با استفاده از storyboard جریان برنامه را به تصویر می کشید، آنچه برای برنامه طراحی می کنید را در حین طراحی و در لحظه مشاهده نموده، می توانید بخش هایی که درست کار می کند و بخش هایی که از عملکرد صحیح برخوردار نیستند را به چشم خود ببینید، تغییرات لازم را بر روی UI اعمال نمایید و مستقیما نتیجه ی آن را مشاهده کنید.

برای مشاهده ی فایل storyboard:

· در کادر project navigator، بر روی فایل Main.storyboard کلیک نمایید. Xcode این فایل را داخل Interface Builder، در ناحیه ی ویرایشگر (editor area) محیط باز می کند.

به پس زمینه ی storyboard در اصطلاح Canvas گفته می شود. می توانید Canvas را برای افزودن و مدیریت چیدمان المان های UI بکار ببرید.

Storyboard می بایست ظاهری مشابه زیر داشته باشد:

image: ../Art/2_storyboard_empty_2x.png

در این مرحله، storyboard تنها یک scene دارد. به یک صفحه از برنامه که حامل محتوا است در اصطلاح scene گفته می شود. پیکانی که به ضلع سمت چپ صفحه ی محتوا یا scene (بر روی canvas) اشاره می کند، نقطه ی شروع storyboard می باشد. بدین معنی که این scene اولین صفحه ای است که به مجرد اجرای اپلیکیشن بارگذاری می شود (اولین صفحه ی محتوای برنامه که جریان و چرخه ی اپلیکیشن از آنجا شروع می شود).

در حال حاضر، scene جاری (که بر روی canvas یا پس زمینه ی storyboard مشاهده می کنید) دربردارنده ی یک view بیشتر نیست. مدیریت این View یا هر Viewدیگری بر عهده ی view controller است. به زودی درباره ی مفهوم view،view controller و وظیفه ی هر یک توضیحاتی را ارائه خواهیم داد.

زمانی که اپلیکیشن خود را بر روی محیط شبیه ساز iPhone 6 در Xcode راه اندازی کردید، آنچه با اجرای برنامه برای شما به نمایش گذاشته شد، در واقع همین view یا محتوایی است که هم اکنون در این scene مشاهده می کنید. اما یک نکته ی جالب توجه: با دقت به scene مورد نظر، متوجه می شوید که ابعاد آن با اندازه ی صفحه نمایش iPhone 6 دقیق همخوانی ندارد. به نظر شما علتش چیست؟ در پاسخ باید گفت که scene قابل مشاهده بر روی پس زمینه یا canvas، یک نمای کلی از رابط کاربری است که برای هر دستگاهی و با هر جهت نمایشی (نمای افقی/عمودی)، قابل استفاده می باشد.

از این نمای کلی برای ایجاد یک adaptive interface یا رابط کاربری سازگار با (اندازه و جهت نمایش) صفحه نمایش دستگاه میزبان استفاده می شود. به عبارت دیگر adaptive interface، همان طور که از نامش پیدا است، یک رابط کاربری انعطاف پذیر است که خود را به صورت اتوماتیک، جهت نمایش دقیق المان های UI، متناسب با صفحه نمایش دستگاه میزبان تنظیم می کند.

ساخت یک رابط کاربری/UI ساده

اکنون به ساخت یک رابط کاربری ساده می پردازیم. ابتدا UI را برای scene (صفحه ی محتوایی) طراحی می کنیم که به ما اجازه می دهد یک آیتم جدید به برنامه ی ثبت و رصد اطلاعات غذا (meal tracking app) ،که آن را FoodTracker نام گذاری کردیم، اضافه نماییم.

Xcode کتابخانه ای از اشیای آماده (object library) دارد که می توانید آن ها را به فایل storyboard اضافه نمایید. برخی از این اشیا، المان های قابل مشاهده هستند که در UI به نمایش درمی آیند. از جمله ی آن های می توان به button و text field اشاره کرد. برخی دیگر مانند view controller ها و gesture recongnizer ها، صرفا رفتار اپلیکیشن را تعریف می کنند و بر روی صفحه نمایش قابل مشاهده نیستند.

المان هایی که در UI برای کاربر قابل مشاهده هستند در اصطلاح view خوانده می شوند. View ها در واقع محتوای اپلیکیشن را برای کاربر به نمایش می گذارند. می توان آن ها را ابزار اصلی و مصالح تشکیل دهنده ی رابط کاربری تلقی کرد که توسط آن محتوا را به صورت کارامد و زیبا در صفحه نمایش به تصویر می کشیم.

View ها طیفی وسیعی از رفتارهای ذاتی و درون ساخته دارند که از جمله ی آن ها می توان از نمایاندن خود بر روی صفحه نمایش و واکنش نشان دادن به ورودی کاربر نام برد.

تمامی view object ها در IOS از جنس UIView یا یکی از کلاس های فرزند (subclass) آن هستند. بسیاری از کلاس هایی که از UIView ارث بری می کنند (subclass های آن محسوب می شوند)، در ظاهر و رفتار منحصر بفرد هستند.

برای شروع یک کادر متن/text field (کلاس UITextField که یکی از کلاس های زیرمجموعه ی UIView است) به scene اضافه می کنیم. همان طور که می دانید، text field یک کادر متن است که به کاربر اجازه می دهد، اسم آیتم جدید (در این برنامه نام غذا) را وارد کند.

برای اضافه کردن یک text field به scene:

1. Object Library را باز نمایید.

Object library در پایین utility area، سمت راست محیط کاری Xcode قابل دسترسی می باشد. اگر Object library قابل مشاهده نبود، می توانید با کلیک بر روی دکمه سوم از سمت چپ در library selector bar، آن را باز کنید (یا می توانید به این روش اقدام نمایید: View > Utilities > Show Object Library).

image: ../Art/object_library_2x.png

یک لیست پدیدار می شود که اسم هر آبجکت را به همراه شرحی درباره ی کاربرد و نمای ظاهری آن نمایش می دهد.

2. در Object library، اسم آبجکت دلخواه (text field) را داخل کادر جستجو (filter field) وارد نمایید. بدین وسیله شی مورد نظر به سرعت در اختیار شما قرار می گیرد.

3. آبجکت Text Field را از Object Library کشیده و بر روی سطح scene جایگذاری کنید.

image: ../Art/2_textfield_drag_2x.png

در صورت لزوم، با طی این مراحل Editor > Canvas > Zoom، زمینه را بزرگ نمایید.

4. بر روی Text field کلیک کرده و آن را در جهت نیمه ی بالایی scene، هم تراز با حاشیه ی سمت چپ بکشید. هنگامی که خط نقطه چین آبی رنگ مانند زیر نمایان شد، کشیدن را متوقف کرده و آبجکت مزبور را رها کنید.

image: ../Art/2_textfield_place_2x.png

راهنمای آبی رنگ به شما کمک می کند آبجکت text field را در جایگاه مناسب آن قرار دهید. در حقیقت این راهنما تنها زمانی پدیدار می شود که آبجکت ها را در نزدیکی آن ها جابجا کرده یا تغییر اندازه دهید. هرگاه آبجکت را رها کنید، راهنمای آبی رنگ نیز محو می شود.

5. جهت تنظیم اندازه ی المان UI مورد نظر، کافی است resize handle آن المان را بکشید. Resize handle یا دستگیره های تنظیم اندازه، مربع های کوچک سفید رنگی هستند که با کلیک بر روی المان مورد نظر، بر روی لبه های آن المان نمایان می شوند.

همان طور که گفته شده برای نمایان شدن resize handle، باید المان را با کلیک بر روی آن انتخاب نمایید. از آنجایی که text field را تازه رها کردیم، المان انتخاب شده و resize handle های آن هنوز قابل مشاهده می باشند.

image: ../Art/2_textfield_resizehandles_2x.png

6. لبه های سمت چپ و راست المان را در جهات مربوطه کشیده تا سه خط عمودی آبی رنگ به صورت زیر نمایان شود.

image: ../Art/2_textfield_finalsize_2x.png

Text field در scene جایگذاری شده، اما هیچ سرنخی که کاربر را در خصوص ورودی مورد انتظار راهنمایی کند، لحاظ نشده است. حال بایستی با استفاده از فیلد placeholder، ویژه ی آبجکت text field، به کاربر اعلان کنیم که در کادر متن اسم یک غذا را درج کند.

جهت تنظیم مقدار فیلد placeholder مربوط به این آبجکت:

1. پس از انتخاب text field، به utility area داخل محیط کاری Xcode مراجعه کرده و Attributes inspector را (در inspector selector bar) با کلیک بر روی دکمه ی سوم از سمت راست، باز نمایید. Attributes inspector به شما امکان می دهد مقدار property های یک آبجکت را در storyboard ویرایش نمایید.

image: ../Art/2_inspector_attributes_2x.png

2. داخل کادر Attributes inspector، فیلدی که Placeholder نام دارد را یافته و مقدار Enter meal name را در آن وارد نمایید.

3. کلید Return را فشار داده تا متن مورد نظر داخل آبجکت text field نمایش داده شود.

پس از اعمال تغییرات نام برده، scene ظاهری مشابه نمونه ی زیر خواهد داشت:

image: ../Art/2_textfield_withplaceholder_2x.png

همزمان با ویرایش attribute های آبجکت text field، همچنین می توانید attribute های صفحه کلیدی که با انتخاب آبجکت ذکر شده نمایان می شود را تغییر دهید.

جهت تنظیم صفحه کلید:

1. Text field را انتخاب نمایید.

2. داخل کادر Attributes inspector، فیلدی که Return Key نام دارد را یافته و مقدار آن را بر روی Done تنظیم کنید. با این کار مقدار پیش فرض Return در صفحه کلید به Done تغییر کرده و بیشتر مورد توجه کاربر قرار می گیرد.

3. حال در کادر Attributes inspector، گزینه ی Auto-enable Return Key را تیک دار نمایید. با فعال نمودن این گزینه، کاربر دیگر قادر نخواهد بود بدون وارد کردن متن در text field (اسم غذا) کلید Done را فشار دهد.

اکنون یک label (از کلاس UILabel) جهت ارائه ی اطلاعات درباره ی کادر متن (آبجکت text field) به بالای آن اضافه می کنیم. همان طور که می دانید label یک المان هوشمند و تعاملی نیست بلکه صرفا یک متن ساده را برای کاربر نمایش می دهد. برای اینکه شما بتوانید نحوه ی تعریف تعامل بین المان های مختلف UI را بهتر درک کنید، این label را طوری تنظیم می کنیم که ورودی کاربر در کادر متن را نمایش دهد. با این کار می توان اطمینان حاصل نمود که المان مورد نظر ورودی کاربر را به درستی دریافت و پردازش می کند.

جهت افزودن یک label به scene:

1. به Object library مراجعه نموده، واژه ی label را در کادر جستجوی (filter field) آن وارد نمایید تا آبجکت Label به سرعت در اختیار شما قرار گیرد.

2. آبجکت Label را از Object library کشیده و بر روی سطح scene جایگذاری کنید.

3. آبجکت نام برده را به بالای کادر متن کشیده و آن را همتراز با حاشیه ی سمت چپ قرار دهید. هنگامی که خط نقطه چین افقی، مانند زیر نمایان می شود، المان را رها کنید.

image: ../Art/2_label_place_2x.png

4. بر روی label دوبار کلیک کرده و سپس واژه ی Meal Name را وارد نمایید.

5. کیلد Return را فشار داده تا متن جدید در label نمایش داده شود.

با اضافه شدن یک المان جدید به scene، ظاهر برنامه به این صورت در خواهد آمد:

image: ../Art/2_label_rename_2x.png

آخرین المانی که به رابط کاربری اضافه می کنیم، یک دکمه (از کلاس UIButton) است. دکمه برخلاف label، یک المان تعاملی می باشد. بدین معنی که با کلیک بر روی آن، فعل یا عملیات خاصی (که شما تعریف می کنید) انجام می شود. در همین مبحث رفتاری (action) برای دکمه تعریف می کنیم که با کلیک بر روی آن، متن label را به مقدار پیش فرض برمی گرداند.

به منظور افزودن المان دکمه به scene:

1. به Object library مراجعه نمایید و پس از یافتن آبجکت Button، آن را کشیده و در سطح scene جایگذاری کنید.

2. حال المان دکمه را کشیده و در زیر کادر متن قرار دهید. زمانی که دو خط نقطه چین آبی رنگ نمایان شد، المان را رها کنید.

image: ../Art/2_button_place_2x.png

3. بر روی دکمه دابل کلیک کرده و عبارت Set Default Label Text را وارد نمایید.

4. کلید Return را فشار دهید. متن جدید بر روی دکمه نمایش داده می شود.

ظاهر جدید برنامه:

image: ../Art/2_button_rename_2x.png

بد نیست پس از افزودن تمامی المان های لازم، چیدمان آن ها در scene را بررسی کنیم. برای این منظور از outline view کمک می گیریم.

جهت مشاهده ی outline view:

1. در Storyboard، دکمه ی toggle (نمایش دادن/پنهان کردن) outline view را پیدا کنید.

image: ../Art/2_outlineview_toggle_2x.png

2. در صورتی که outline view در حالت collapsed قرار داشته باشد (پنهان باشد)، بر روی دکمه ی toggle کلیک کرده تا کادر outline view باز شود. با این دکمه می توانید outline view را با توجه به نیاز باز کرده و یا آن را جمع کنید.

Outline view – کادری که در سمت چپ canvas نمایش داده می شود – به شما اجازه می دهد اشیای موجود در storyboard را با نمای درختی (به صورت سلسله مراتب) مشاهده نمایید. به عبارت بهتر در این کادر می توانید المان هایی که به رابط کاربری برنامه خود اضافه کردید را در سلسله مراتب مشاهده نمایید. با دیدن این سلسله مراتب در کادر مذکور، شاید این سوال پیش آید که چرا المان های UI در زیر گره ی View لیست شده اند (به عبارت دیگر داخل view دیگر گنجانده شده اند)؟

View ها علاوه بر نمایش محتوا و تعامل با کاربر، می توانند ظرفی برای نگهداری سایر view ها باشند. View ها در قالب یک سلسله مراتب نمایش داده می شوند که در اصطلاح view hierarchy خوانده می شود. view hierarchy چیدمان و ترتیب view ها را نسبت به دیگر view ها مشخص می کند. داخل این سلسله مراتب، view هایی که داخل view دیگری جای گرفته اند subview نامیده و view هایی که دربرگیرنده و میزبان view های دیگر هستند superview خوانده می شوند. یک view می تواند چندین subview داشته باشد، اما خود زیرمجموعه ی تنها یک superview باشد.

image: ../Art/2_outlineview_2x.png

به طور کلی هر scene یک view hierarchy دارد. در بالاترین مرتبه ی هر view hierarchy نیز یک content view مشاهده می شود. برای مثال در scene جاری، View ای که در بالاترین مرتبه قرار دارد، content view نامیده می شود و همان طور که می بینید خود داخل View Controller قرار دارد. text field، label، button و هر المان دیگری که داخل این scene قرار می دهید زیرمجموعه ی content view خواهد بود (هرچند این المان ها نیز می توانند view های دیگری به عنوان زیرمجموعه ی خود داشته باشند).

پیش مشاهده ی User Interface برنامه

اپلیکیشن خود را در فواصل زمانی معین بررسی کرده و مطمئن شوید همه چیز مورد انتظار و دلخواه شما می باشد. به عنوان مثال جهت پیش مشاهده ی رابط کاربری برنامه ی خود می توانید از ابزاری به نام assistant editor بهره بگیرید. این ابزار یک ویرایشگر کمکی در کنار ویرایشگر اصلی Xcode به نمایش در می آورد که در آن می توانید نمایی از برنامه ی خود را مشاهده نمایید.

جهت پیش مشاهده ی رابط کاربری برنامه ی خود:

1. بر روی دکمه ی اشاره شده در تصویر زیر کلیک نموده و ابزار assistant editor را باز کنید (این دکمه در نوار ابزار Xcode، بالای محیط گوشه سمت راست قابل مشاهده و دسترسی می باشد).

image: ../Art/assistant_editor_toggle_2x.png

2. اگر در محیط به فضای کاری بیشتری نیاز دارید، در آن صورت می توانید project navigator و utility area را با کلیک بر روی دکمه های مربوطه در نوار ابزار محیط (اشاره شده در تصویر زیر) کوچک نمایید.

image: ../Art/navigator_utilities_toggle_on_2x.png

در صورت نیاز می توانید outline view را نیز جمع نمایید.

3. در editor selector bar، مقیم در بالای assistant editor (اشاره شده در تصویر زیر)، با طی کردن مراحل زیر، assistant editor را از حالت Automatic بر روی Preview تنظیم نمایید: Preview > Main.storyboard (Preview) .

image: ../Art/2_assistant_editorselectorbar_2x.png

همان طور که در assistant editor مشاهده می کنید، طول المان text field از لبه ی صفحه ی جاری بیرون می زند. اما اگر به خاطر داشته باشید، رابط کاربری که در storyboard تعریف کردیم کاملا بی نقص نمایش داده می شود. پس چرا همین رابط کاربری در حالت preview یا پیش نمایش iPhone متوازن نیست و به طور صحیح مقیاس دهی نمی شود؟

image: ../Art/2_preview_2x.png

همان طور که قبلا ذکر شد، رابط کاربری که طراحی می کنیم، در واقع یک interface انعطاف پذیر و قابل تنظیم است که متناسب با اندازه ی دستگاه میزبان (صفحه نمایش دستگاه های iPhone و iPad) مقیاس بندی می شود. scene یا صفحه ی محتوایی که به صورت پیش فرض در storyboard می بینید عملا یک نما و نسخه ی کلی از UI برنامه ی شما را به نمایش می گذارد. اما در بستر assistant editor می بایست نحوه ی نمایش و مقیاس interface را برای دستگاه های مختلف (فضای موجود و اندازه ی صفحه نمایش مربوطه) درنظر گرفته و مشخص نمایید. برای مثال، زمانی که رابط کاربری بایستی خود را برای تطبیق با اندازه ی صفحه نمایش دستگاه iPhone کوچک کند، المان text field نیز باید همراه با آن کوچک شود و یا زمانی که می بایست جهت تطبیق با صفحه نمایش iPad بزرگ شود، المان مزبور باید متناسب با آن مقیاس بندی شود.

برای تعیین قوانین مقیاس دهی و تنظیم رابط کاربری متناسب با دستگاه، می توانید از موتور نمایش Auto Layout استفاده نمایید.

استفاده از موتور نمایش Auto Layout

Auto Layout یک layout engine یا موتور نمایش و رندرینگ قدرتمند است که به شما امکان می دهد به آسانی layout (قالب های) انعطاف پذیر و قابل تنظیم طراحی نمایید. بدین صورت است که شما خواسته یا ایده ی خود را در خصوص موقعیت دهی المان ها در یک scene توصیف می کنید و سپس به layout engine واگذار می کنید خود تصمیم بگیرد چگونه به بهترین نحو این ایده را پیاده سازی کند. تمایل خود در خصوص چیدمان المان ها را بایستی در قالب constraint ها بیان کنید – constraint ها قوانینی هستند که مشخص می کنند المان ها چگونه باید نسبت به دیگر المان ها موقعیت دهی شوند، چه اندازه ای داشته باشند و در صورت کاهش فضای موجود، کدام المان اول کوچک شود.

یکی دیگر از ابزار قدرتمندی که همراه با Auto Layout، در راستای مدیریت چیدمان و تنظیم اندازه ی المان متناسب با اندازه ی دستگاه (کار با layout) بکار می رود، stack view (کلاس UIStackView) می باشد. stack view یک رابط ساده و پربازده برای نمایش و چیدمان مجموعه ای از view ها در قالب ستون یا سطر ارائه می دهد. این ابزار به شما اجازه می دهد از قدرت Auto Layout بهترین استفاده را کرده و UI هایی طراحی کنید که به صورت پویا و در لحظه خود را با جهت (نمای افقی/عمودی)، اندازه ی صفحه نمایش و سایر تغییراتی که در فضای موجود ایجاد می شود، تطبیق دهد.

شما می توانید به آسانی رابط جاری را در یک stack view گنجانده، سپس با اعمال constraint های لازم اطمینان حاصل نمایید این stack view در دستگاه های مختلف به درستی نمایش داده می شود.

جهت افزودن constraint های مورد نیاز به scene جاری:

1. ابتدا می بایست با کلیک بر روی دکمه ی Standard، اشاره شده در تصویر زیر، Standard editor را باز نمایید.

image: ../Art/standard_toggle_2x.png

با کلیک بر روی دکمه های Navigator و Utilities در نوار ابزار محیط Xcode، کادر project navigator و utility area را باز نمایید.

2. کلید Shift را نگه داشته و سپس المان های text field، label و button را انتخاب نمایید.

image: ../Art/2_AL_shift_select_2x.png

3. در پایین canvas، سمت راست بر روی دکمه یStack کلیک نمایید (و یا این مسیر را طی کنید: Editor > Embed In > Stack View).

image: ../Art/2_AL_stackmenu_2x.png

پس از کلیک بر روی دکمه ی مزبور، Xcodeالمان های UI را به صورت پشته بر روی هم قرار داده یا به اصطلاح آن ها را در قالب یکstack view می گنجاند (wrap می کند). با بررسی و تحلیل layout یا طرح بندی جاری، Xcode تصمیم می گیرد که المان ها را باید به صورت عمودی بر روی هم انباشته و نمایش دهد (نه به صورت افقی).

image: ../Art/2_AL_stack_2x.png

4. در صورت لزوم، می توانید outline view را باز کرده، سپس آبجکت Stack View را انتخاب نمایید.

image: ../Art/2_AL_outlineview_2x.png

5. در Attribute inspector، داخل فیلد Spacing مقدار 12 را وارد نمایید. حال کلید Return را فشار دهید. خواهید دید که المان های UI به اندازه ی مقداری که در فیلد نام برده درج کردید، به صورت عمودی از هم فاصله داده شده و همزمان با آن stack view نیز بزرگتر می شود.

image: ../Art/2_AL_stackspaced_2x.png

6. در پایین canvas، سمت راست، با کلیک بر روی دکمه ی سوم (از سمت چپ) منوی Pin را باز نمایید.

image: ../Art/2_AL_pinmenu_2x.png

7. در بالای عبارت “Spacing to nearest neighbor” در تصویر زیر، با کلیک بر روی دو constraint افقی و همچنین constraint عمودی سمت بالا، آن ها را انتخاب نمایید. constraint های نام برده پس از انتخاب قرمز رنگ می شوند.

image: ../Art/2_AL_pinconstraints_2x.png

این Constraint ها در واقع نشانگر میزان فاصله المان جاری با المان های مجاور است. در اینجا منظور از اصطلاح nearest neighbor، لبه ی نزدیکترین المان UI به عنصر مورد نظر است که ممکن است superview، peer view یا صرفا یک margin (حاشیه ی پس زمینه یا canvas) باشد. از آنجایی که گزینه ی “Constrain to margins” انتخاب شده، stack view به حاشیه ی سمت چپ، راست و بالای المان superview چسبانده شده و فاصله ی اندکی از لبه ی سمت چپ صفحه را خالی می گذارد.

8. در کادرهای سمت چپ و راست مقدار 0 و در کادر سمت بالا مقدار 60 را وارد نمایید.

9. از منوی pop-up جنب عبارت Update Frames، گزینه ی Items of New Constraints را انتخاب نمایید. منوی Pin هم اکنون می بایست ظاهری مشابه زیر داشته باشد:

image: ../Art/2_AL_stackconstraints_2x.png

10. در منوی Pin، بر روی دکمه ی Add 3 Constraints کلیک نمایید.

image: ../Art/2_AL_add3constraints_2x.png

رابط کاربری برنامه هم اکنون باید چنین بنظر برسد:

image: ../Art/2_AL_stackfinal_2x.png

همان طور که می بینید که المان text field مانند قبل تا انتهای scene (لبه ی سمت راست) کشیده نمی شود. در زیر به تنظیم پهنای المان ذکر شده می پردازیم.

جهت تنظیم عرض المان text field در scene جاری:

1. (داخل storyboard) ابتدا المان مورد نظر را در scene فعلی انتخاب نمایید.

2. در پایین canvas، سمت راست، با کلیک بر روی دکمه ی سوم (از سمت چپ) منوی Pin را باز نمایید.

image: ../Art/AL_pinmenu_2x.png

3. در بالای عبارت “Spacing to nearest neighbor”، با کلیک بر روی دو constraint افقی، آن ها را انتخاب نمایید. همان طور که قبلا گفته شد، constraint ها پس از انتخاب به رنگ قرمز پر رنگ در می آیند.

4. داخل کادرهای سمت راست و چپ، مقدار 0 را وارد نمایید.

5. از منوی pop-up، جنب عبارت Update Frames، گزینه ی Items of New Constraints را انتخاب نمایید. در حال حاضر منوی Pin می بایست اینگونه به نظر برسد:

image: ../Art/2_AL_textfieldconstraints_2x.png

6. در منوی Pin، بر روی دکمه ی Add 2 Constraints کلیک نمایید.

image: ../Art/2_AL_add2constraints_2x.png

7. در حالی که text field را انتخاب کرده اید، بر روی آیکون در utility area کلیک نموده و Size inspector را باز نمایید. Size inspector به شما امکان می دهد، اندازه و مکان قرارگیری (position) آبجکت مورد نظر را در storyboard ویرایش و تنظیم نمایید.

image: ../Art/2_inspector_size_2x.png

8. از فیلد Intrinsic Size، گزینه ی Placeholder را انتخاب نمایید. (این فیلد در پایین Size inspector قرار گرفته، بنابراین جهت دسترسی به آن باید با نوار پیمایش به پایین کادر بروید.)

لازم به ذکر است که Text field بر اساس محتوای درونی خود اندازه بندی می شود. اندازه ی این المان بر اساس ویژگی intrinsic content size المان مشخص می شود (intrinsic content size به حداقل فضای لازم برای نمایش کامل و بی نقص تمامی محتویات view اشاره دارد).

در مواقعی که فکر می کنید اندازه ی المان UI ممکن است بزرگتر یا کوچکتر از مورد انتظار (در زمانی طراحی) باشد، می توانید فیلد intrinsic size آن را بر روی مقدار placeholder تنظیم نمایید. در حال حاضر، تنها محتوای text field، یک رشته ی مکان نگهدار (placeholder string) بوده که اندازه ی آن احتمالا از طول رشته ای که کاربر وارد می کند، کم تر است.

رابط کاربری برنامه در زمان حاضر می بایست مشابه زیر باشد:

image: ../Art/2_AL_textfieldfinal_2x.png

اکنون برنامه ی خود را در محیط شبیه ساز یا Simulator اجرا کنید. المان text field قاعدتا دیگر نباید از چارچوب صفحه ی جاری بیرون بزند. شما بایستی بتوانید داخل text field کلیک کرده و متن مورد نظر را با استفاده از صفحه کلیدوارد نمایید (در صورت تمایل می توانید صفحه کلید نرم افزار را با زدن Command-K به صورت toggle نمایش داده/پنهان نمایید). حال اگر دستگاه را بچرخانید (با فشردن کلیدهای Command-Left Arrow یا Command-Right Arrow) و یا برنامه را بر روی دستگاه دیگری اجرا کنید، خواهید دید که text field متناسب با جهت و اندازه ی صفحه نمایش دستگاه میزبان، کوچک/بزرگ می شود. لازم به ذکر است که در نمای افقی (landscape orientation) نوار وضعیت/status bar از نظر کاربر محو می شود.

image: ../Art/2_sim_finalUI_2x.png

چنانچه اپلیکیشن رفتار مورد انتظار را ارائه نداد (المان ها را آن طور که باید نمایش نداد)، می توانید از امکانات debugging موتور Auto Layout جهت خطایابی و اشکال زدایی بهره بگیرید. برای این منظور بر روی آیکون Resolve Auto Layout Issues کلیک نموده، سپس گزینه ی Reset to Suggested Constraints را انتخاب نمایید تا Xcode رابط کاربری برنامه ی شما را بروز آوری کرده و constraint های مناسب را اعمال کند.

یا پس از کلیک بر روی آیکون Resolve Auto Layout Issues، گزینه ی Clear Constraints را انتخاب نمایید تا تمامی constraint های اعمال شده بر روی المان های UI حذف گشته و سپس تمامی مراحل فوق را مجددا دنبال نمایید.

image: ../Art/2_AL_resolvemenu_2x.png

scene جاری به جز نمایش تعداد محدودی المان UI، کار به خصوصی انجام نمی دهد. با این حال نمایی از یک رابط کاربری ساده و کاربردی ارائه می دهد که می تواند شما را در طراحی UI برنامه ی خود راهنمایی کند.

ساخت یک layout (قالب و نمای کلی) کارامد و قابل تنظیم (که متناسب با عرض صفحه ی دستگاه میزبان و جهت نمایش، بزرگ/کوچک می شود) می تواند پایه ی قوی برای برنامه باشد. شما می توانید بر اساس این پایه ی قوی نسبت به ساخت دیگر بخش های برنامه اقدام نمایید.

ewew

اضافه کردن models و controller

در این قسمت، ما کلاس های model را برای مشخص کردن ماهیت دیتابیس تعریف می کنیم. سپس شما می توانید Controller های Web API که عملیات CRUD روی آن موجودیت ها را انجام می دهد اضافه کنید.

اضافه کردن کلاس های Model

در این مقاله ما با استفاده از Code First و EntityFramework دیتابیس خود را می سازیم. با Code First شما می توانید کلاس های C# را که با جداول دیتابیس مطابقت دارند با EF بسازید.

ما کار خود را با تعریف object ها به عنوان POCO شروع می کنیم. در ادامه POCO های زیر را خواهیم ساخت:

·         نویسنده

·         کتاب

در solution explorer روی فولدر models کلیک راست کنید و Add را انتخاب کنید و سپس Class را بزنید و نام آن را Author بگذارید.

clip_image002

قطعه کد زیر را با کدهای درون Author.cs جایگزین کنید.

using System.Collections.Generic;

using System.ComponentModel.DataAnnotations;

namespace BookService.Models

{

    public class Author

    {

        public int Id { get; set; }

        [Required]

        public string Name { get; set; }

    }

}

کلاس دیگری با نام Book بسازید و به صورت زیر کد نویسی کنید.

using System.ComponentModel.DataAnnotations;

namespace BookService.Models

{

    public class Book

    {

        public int Id { get; set; }

        [Required]

        public string Title { get; set; }

        public int Year { get; set; }

        public decimal Price { get; set; }

        public string Genre { get; set; }

        // Foreign Key

        public int AuthorId { get; set; }

        // Navigation property

        public Author Author { get; set; }

    }

}

Entityframework از این مدل ها برای ساخت جداول دیتابیس استفاده می کند. برای هر مدل،خاصیت Id در نقش کلید اصلی جدول دیتابیس می باشد.

در کلاس book، AuthorId یک کلید خارجی در جدول Author تعریف می کند.(برای سادگی، فرض می کنیم هر book تنها یک author دارد.) همچنین کلاس book شامل خاصیت جهت یابی (navigation)برای Author مربوط می باشد. شما می توانید از خاصیت  navigationبرای دسترسی به Author مربوط استفاده کنید.

اضافه کردن Comtroller های Web API

در این قسمت، ما Controller هایی اضافه می کنیم که عملیات CRUD(ساختن،خواندن،به روز رسانی و حذف کردن) را پشتیبانی کنند. Controller از Entity Framework برای ارتباط با لایه دیتابیس استفاده می کنیم.

ابتدا، شما می توانید فایل Controllers/ValuesController.cs را حذف کنید. در این فایل یک مثال از Controller Web API موجود است که در این مقاله به آن نیاز نداریم.

clip_image003

در مرحله بعد، پروژه را Build کنید. روش Scaffold کردن Web API از reflection برای پیدا کردن کلاس های model استفاده می کند و برای همین به کامپایل نیاز دارد.

در solution explorer روی فولدر Models  کلیک راست کنید و ابتداAdd  و سپس Controller را انتخاب کنید.

clip_image005

در پنجره Add Scaffold، Web API 2 Controller with actions, using Entity Framework را انتخاب کرده و Ok کنید.

clip_image007

در پنجره Add Controller مراحل زیر را انجام دهید:

1- در لیست Model Class، کلاس Author را انتخاب کنید(اگر این کلاس را پیدا نکردید، دوباره به برنامه خود بازگردید و دوباره Build کنید.)

2- گزینه Use async controller actions را انتخاب کنید.

3- نام Controller را AuthorsController انتخاب کنید.

4- دکمه (+) را برای Data Context Class بزنید.

clip_image009

در پنجره New Data Context ، نام پیش فرض را قرار داده و روی Add کلیک کنید.

clip_image010

برای تکمیل فرآیند پنجره Add Controller، Add را کلیک کنید. این پنجره دو کلاس به پروژه شما اضافه می کند:

·         AuthorsController  یک Controller Web API تعریف می کند. Controller REST API لیست author برای اجرای عملیات CRUD کاربران پیاده سازی می کند.

·         BookServiceContext  موجودیت  object ها را در زمان اجرا مدیریت می کند که شامل پر کردن دیتابیس با object ها، تغییر ردیابی و تداوم داده در دیتابیس می شود. این از DbContex ارث بری می کند.

clip_image011

در این قسمت، پروژه را دوباره build کنید.حالا دوباره این مراحل را برای اضافه کردن Controller API  برای موجودیت های Book طی کنید. در اینجا، Book را به عنوان کلاس model انتخاب کنید و کلاس BookServiceContext  موجود را برای data context انتخاب کنید(data context جدید نسازید) برای اضافه کردن Controller ، Add کنید.

clip_image013

 

 

mr

دوره آموزشی PHP

 کلیه حقوق مادی و معنوی این مقاله متعلق به آموزشگاه تحلیل داده می باشد و هر گونه استفاده غیر قانونی از آن پیگرد قانونی دارد.

آموزش Collection ها در laravel

1. مقدمه

2. نحوه ی ایجاد نمونه ی جدید از کلاس Collection

3. شرح توابع موجود در کلاس  Collection

مقدمه

کلاس Illuminate\Support\Collection از فریم ورک Laravel یک wrapper کارآمد و بهینه برای کار با داده های از نوع آرایه در اختیار برنامه نویس قرار می دهد (wrapper = هر کلاسی که قابلیت و امکانات یک کلاس یا کامپوننت دیگری را در خود کپسوله سازی کند). برای فهم بهتر توجه خود را به مثال ساده ی زیر جلب نمایید.

در این مثال تابع کمکی (helper) collect را فراخوانده و به وسیله ی آن نمونه ای جدید از کلاس collection ایجاد می کنیم. سپس تابع strtoupper را برای هر المان اجرا نموده و تمامی المان های تهی (با مقدار null) را حذف می کنیم:  

$collection = collect(['taylor', 'abigail', null])->map(function ($name) {

    return strtoupper($name);

})

->reject(function ($name) {

    return empty($name);

});

همان طور که از مثال فوق مشاهده می شود، کلاس Collection به شما اجازه می دهد متدهایش را (با اتصال آن ها به انتهای یکدیگر) به صورت زنجیره ای فراخوانی نموده و آرایه را (علاوه بر ویرایش) به آسانی به فیلد مزبور نگاشت (map) نمایید. شایان توجه است که هر متد Collection به طور کلی نمونه ی کاملا جدید از کلاس Collection را در خروجی برمی گرداند. 

نحوه ی ایجاد نمونه ی جدید از کلاس Collection

همان طور که در بالا نیز ذکر شد، تابع کمکی collect یک نمونه ی کاملا جدید از کلاس Illuminate\Support\Collection را در خروجی برمی گرداند. نحوه ی ایجاد collection جدید به صورت زیر می باشد:

$collection = collect([1, 2, 3]);

به طور پیش فرض، collection های مدل های Eloquent در قالب نمونه هایی جدید از کلاس Collection به عنوان خروجی برگردانده می شوند. البته شما آزادید هر زمان که اپلیکیشن استفاده از Collection را ایجاب می کند، از این کلاس استفاده نمایید. 

شرح توابع موجود در کلاس  Collection

در زیر به شرح تمامی متدهای کلاس Collection می پردازیم. یادآور می شویم که تمامی متدهای این کلاس را می توان به منظور دستکاری و ویرایش آسان آرایه به یکدیگر متصل نموده و به صورت زنجیره ای فراخوانی کرد.

بعلاوه تقریبا تمامی متدهای Collection یک نمونه ی جدید از کلاس مزبور را به عنوان خروجی برگردانده تا نمونه ی اصلی collection در صورت لزوم حفظ شده و برای استفاده در آینده در دسترس باشد.

 

فهرست متدهای کلاس Collection

متد all()     

کل آرایه ی اصلی را به عنوان خروجی برمی گرداند: 

collect([1, 2, 3])->all();

// [1, 2, 3]

متد avg()

متد avg میانگین مقادیر موجود در collection را محسابه کرده و در خروجی برمی گرداند:

collect([1, 2, 3, 4, 5])->avg();

// 3                                                                                                                                                                                          

در صورتی که collection دربردارنده ی آرایه یا اشیا تودرتو باشد، بایستی یک کلید در داخل آرایه برای مشخص کردن مقادیری که می خواهید میانگین آن محاسبه شود، ارسال کنید: 

$collection = collect([

    ['name' => 'JavaScript: The Good Parts', 'pages' => 176],

    ['name' => 'JavaScript: The Definitive Guide', 'pages' => 1096],

]);

$collection->avg('pages');

// 636

Chunk()

این متد یک مجموعه (آرایه) کامل را بر اساس مقداری که به عنوان آرگومان به آن ارسال می شود، به بخش های کوچکتر با اندازه ی مشخص تقسیم می کند: 

$collection = collect([1, 2, 3, 4, 5, 6, 7]);

$chunks = $collection->chunk(4);

$chunks->toArray();

// [[1, 2, 3, 4], [5, 6, 7]]

این متد به خصوص داخل view ها و زمانی که با یک سیستم نمایش gird مانند، همچون bootstrap کار می کنید بسیار کارآمد و مفید واقع می شود. فرض کنید یک مجموعه از مدل های Eloquent در دست دارید و می خواهید آن ها به صورت grid را نمایش دهید:

@foreach ($products->chunk(3) as $chunk)

    <div class="row">

        @foreach ($chunk as $product)

            <div class="col-xs-4">{{ $product->name }}</div>

        @endforeach

    </div>

@endforeach

Collapse()

متد collapse مجموعه ای از چندین آرایه ی مجزا را در قالب یک collection واحد و تک بعدی در خروجی برمی گرداند:

$collection = collect([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);

$collapsed = $collection->collapse();

$collapsed->all();

// [1, 2, 3, 4, 5, 6, 7, 8, 9]

Combine()

متد combine کلیدهای یک مجموعه را با مقادیر آرایه یا مجموعه ی دیگر ترکیب می کند:

$collection = collect(['name', 'age']);

$combined = $collection->combine(['George', 29]);

$combined->all();

// ['name' => 'George', 'age' => 29]

 

 

Contains()

متد contains بررسی می کند آیا مجموعه ی مورد نظر حاوی مقدار خاصی هست یا خیر و در صورت وجود مقدار مورد درخواست، true را برمی گرداند:  

$collection = collect(['name' => 'Desk', 'price' => 100]);

$collection->contains('Desk');

// true

$collection->contains('New York');

// false

همچنین می توانید یک جفت کلید / مقدار به عنوان پارامتر به متد contains ارسال کنید. متد نام برده به وسیله ی پارامتر ارسالی بررسی می کند آیا جفت کلید/مقدار ارائه شده در مجموعه وجود دارد یا خیر:

$collection = collect([

    ['product' => 'Desk', 'price' => 200],

    ['product' => 'Chair', 'price' => 100],

]);

$collection->contains('product', 'Bookcase');

// false

همچنین می توانید یک تابع (بازفراخوان) callback به عنوان پارامتر برای بررسی وجود یا عدم وجود مقدار مورد نظر در مجموعه به تابع contains() ارسال کنید: 

$collection = collect([1, 2, 3, 4, 5]);

$collection->contains(function ($key, $value) {

    return $value > 5;

});

// false

متد count()

تعداد کل آیتم های موجود در یک مجموعه را به عنوان خروجی برمی گرداند:

$collection = collect([1, 2, 3, 4]);

$collection->count();

// 4

متد diff()

متد diff دو مجموعه (collection) را با یکدیگر یا یک مجموعه را با یک آرایه ی متعارف PHP مقایسه می کند و تفاوت بین آن ها را در خروجی برمی گرداند:

$collection = collect([1, 2, 3, 4, 5]);

$diff = $collection->diff([2, 4, 6, 8]);

$diff->all();

// [1, 3, 5]

each()

متد حاضر داخل آیتم های موجود در مجموعه حلقه زده و تمامی آیتم ها را به ترتیب و به صورت جداگانه به تابع callback ارسال می کند:

$collection = $collection->each(function ($item, $key) {

    //

});

 

برای خروج از حلقه باید مقدار false را از تابع callback برگردانید:

$collection = $collection->each(function ($item, $key) {

    if (/* some condition */) {

        return false;

    }

});

متد every()

این متد یک مجموعه ی جدید متشکل از تمامی المان های مشخص شده توسط پارامتر ارسالی را در خروجی برمی گرداند:

$collection = collect(['a', 'b', 'c', 'd', 'e', 'f']);

$collection->every(4);

// ['a', 'e']

 

 

در صورت تمایل می توانید اندیس شروع یا نقطه ی برش (offset) را به عنوان پارامتر دوم به متد پاس دهید:

$collection->every(4, 1);

// ['b', 'f']

متد except()

متد except همان طور که از نامش پیداست تمامی آیتم های موجود در مجموعه را به استثنای آن هایی که به عنوان پارامتر به متد پاس داده شده اند را به عنوان خروجی برمی گرداند:

$collection = collect(['product_id' => 1, 'name' => 'Desk', 'price' => 100, 'discount' => false]);

$filtered = $collection->except(['price', 'discount']);

$filtered->all();

// ['product_id' => 1, 'name' => 'Desk']

متد except درست برعکس متد only عمل می کند.

متد filter()

این متد یک مجموعه را بر اساس شرط مشخص شده در تابع callback فیلتر کرده و تنها آن المان هایی را به عنوان خروجی برمی گرداند که با شرط منطبق باشند (مثلا همگی از دو بزرگتر باشند):

$collection = collect([1, 2, 3, 4]);

$filtered = $collection->filter(function ($value, $key) {

    return $value > 2;

});

$filtered->all();

// [3, 4]

این متد نقطه ی مقابل متد reject می باشد.

fitst()

این متد اولین المانی که با شرط معینی همخوانی داشته باشد را به عنوان خروجی برمی گرداند:

collect([1, 2, 3, 4])->first(function ($key, $value) {

    return $value > 2;

});

// 3

همچنین می توانید متد first را بدون آرگومان صدا بزنید که در این صورت اولین المان موجود در مجموعه در خروجی بازیابی می شود. در صورتی که collection تهی باشد، مقدار null برگردانده می شود: 

collect([1, 2, 3, 4])->first();

// 1

متد flatmap()

متد flatmap() داخل یک collection حلقه زده و سپس تک تک المان های آن را به تابع callback ارسال می کند. تابع بازفراخوان (callback) می تواند آیتم های ارسالی را ویرایش کرده و برگرداند. بدین وسیله مجموعه ای جدید از آیتم های ویرایش شده به عنوان خروجی برگردانده می شود. در نهایت آرایه ها flatten شده و در قالب یک آرایه ی واحد و تک بعدی در خروجی به نمایش در می آید:

$collection = collect(

    ['name' => 'Sally'],

    ['school' => 'Arkansas'],

    ['age' => 28]

]);

$flattened = $collection->flatMap(function ($values) {

    return strtoupper($values);

});

$flattened->all();

// ['name' => 'SALLY', 'school' => 'ARKANSAS', 'age' => 28];

متد flatten()

این متد یک مجموعه ی چند بعدی را به یک collection تک بعدی تبدیل می کند:

$collection = collect(['name' => 'taylor', 'languages' => ['php', 'javascript']]);

$flattened = $collection->flatten();

$flattened->all();

// ['taylor', 'php', 'javascript'];

 

flip()

این متد کلیدهای یک مجموعه را با مقادیر مربوطه ی آن جابجا (جایگزین) می کند (جای کلید و مقدار را با هم عوض می کند):

$collection = collect(['name' => 'taylor', 'framework' => 'laravel']);

$flipped = $collection->flip();

$flipped->all();

// ['taylor' => 'name', 'laravel' => 'framework']

forget()

متد forget() یک آیتم را بر اساس کلید آن که به عنوان آرگومان پاس داده می شود از مجموعه حذف می نماید:

$collection = collect(['name' => 'taylor', 'framework' => 'laravel']);

$collection->forget('name');

$collection->all();

// ['framework' => 'laravel']

نکته: برخلاف دیگر متدهای collection، forget() یک نمونه ی ویرایش شده از مجموعه ی اصلی را به عنوان خروجی بر نمی گرداند. بلکه تنها آن مجموعه ای که برای آن فراخوانده می شود را با حذف آیتمی از آن ویرایش می کند. 

 

ForPage()

متد forPage یک مجموعه ی جدید حاوی آیتم هایی که در صفحه ی مورد نظر با شماره ی معین موجود می باشد را به عنوان نتیجه ی بازگشتی ارائه می دهد:

$collection = collect([1, 2, 3, 4, 5, 6, 7, 8, 9]);

$chunk = $collection->forPage(2, 3);

$chunk->all();

// [4, 5, 6]

این متد شماره ی صفحه  و تعداد آیتم هایی که به ازای هر صفحه باید نمایش داده شود را به ترتیب به عنوان آرگومان های ورودی می پذیرد.

متد get()

این متد آیتمی که دارای کلید معین هست را به عنوان خروجی برمی گرداند (کلید را به عنوان آرگومان پذیرفته و در خروجی مقدار مربوطه را برمی گرداند). در صورتی که کلید مورد نظر در آرایه موجود نباشد، مقدار null را به عنوان خروجی برمی گرداند:

$collection = collect(['name' => 'taylor', 'framework' => 'laravel']);

$value = $collection->get('name');

// taylor

همچنین می توان یک callback به عنوان مقدار پیشفرض ارسال کرد. در صورتی که کلید مورد نظر در آرایه موجود نباشد مقدار callback را به عنوان خروجی برمی گرداند:

$collection->get('email', function () {

    return 'default-value';

});

// default-value

 

متد groupBy()

این متد آیتم های یک مجموعه را بر اساس کلید ارسال شده به عنوان آرگومان گروه بندی می کند:

 

$collection = collect([

    ['account_id' => 'account-x10', 'product' => 'Chair'],

    ['account_id' => 'account-x10', 'product' => 'Bookcase'],

    ['account_id' => 'account-x11', 'product' => 'Desk'],

]);

$grouped = $collection->groupBy('account_id');

$grouped->toArray();

/*

    [

        'account-x10' => [

            ['account_id' => 'account-x10', 'product' => 'Chair'],

            ['account_id' => 'account-x10', 'product' => 'Bookcase'],

        ],

        'account-x11' => [

            ['account_id' => 'account-x11', 'product' => 'Desk'],

        ],

    ]

*/

علاوه بر ارسال یک key از نوع رشته، می توانید یک تابع callback را به عنوان آرگومان ارسال کنید. تابع callback بایستی مقداری که می خواهید گروه بر اساس آن کلید گروه بندی شوند را به عنوان خروجی برگرداند:

$grouped = $collection->groupBy(function ($item, $key) {

    return substr($item['account_id'], -3);

});

$grouped->toArray();

/*

    [

        'x10' => [

            ['account_id' => 'account-x10', 'product' => 'Chair'],

            ['account_id' => 'account-x10', 'product' => 'Bookcase'],

        ],

        'x11' => [

            ['account_id' => 'account-x11', 'product' => 'Desk'],

        ],

    ]

*/

 

متد has()

این متد بررسی می کند آیا کلید معینی در مجموعه وجود دارد یا خیر:

$collection = collect(['account_id' => 1, 'product' => 'Desk']);

$collection->has('email');

// false

 

 

متد implode()

 

متد implode دو آیتم در یک مجموعه را به هم پیوند می زند (مقدار دو کلید را به یکدیگر join می کند). آرگومان های این متد به نوع آیتم های موجود در مجموعه بستگی دارد.

 

چنانچه آیتم های داخل مجموعه از نوع شی یا آرایه باشند، در آن صورت بایستی کلید آن attribute هایی را که مایلید به یکدیگر پیوند زده شده و نیز تفکیک گری که باید بین دو مقدار قرار گیرد را به ترتیب به عنوان آرگومان های اول و دوم به تابع implode() ارسال کنید. 

$collection = collect([

    ['account_id' => 1, 'product' => 'Desk'],

    ['account_id' => 2, 'product' => 'Chair'],

]);

$collection->implode('product', ', ');

// Desk, Chair

حال اگر مجموعه حاوی مقادیر رشته ای و عددی باشد، کافی است تفکیک گر (برای مثال '-') را به عنوان آرگومان به تابع ارسال کنید:

collect([1, 2, 3, 4, 5])->implode('-');

// '1-2-3-4-5'

 

متد intersect()

متد intersect() تمامی مقادیری که در array یا collection  مورد نظر موجود نیستند را حذف می کند:

$collection = collect(['Desk', 'Sofa', 'Chair']);

$intersect = $collection->intersect(['Desk', 'Chair', 'Bookcase']);

$intersect->all();

// [0 => 'Desk', 2 => 'Chair']

همان طور که می بینید مجموعه ی خروجی کلیدهای collection اصلی را نگه می دارد.

متد isEmpty()

این متد در صورتی که collection مورد نظر تهی باشد مقدار true را برمی گرداند و در غیر این صورت false را بازیابی می کند:

collect([])->isEmpty();

// true

 

متد keyBy()

متد  keyByیک مجموعه را بر اساس کلیدی که به عنوان آرگومان به آن پاس داده شده، کلید دهی (key) می کند: 

$collection = collect([

    ['product_id' => 'prod-100', 'name' => 'desk'],

    ['product_id' => 'prod-200', 'name' => 'chair'],

]);

 

$keyed = $collection->keyBy('product_id');

$keyed->all();

/*

    [

        'prod-100' => ['product_id' => 'prod-100', 'name' => 'Desk'],

        'prod-200' => ['product_id' => 'prod-200', 'name' => 'Chair'],

    ]

*/

در صورتی که چندین آیتم دارای کلید یکسان باشند، تنها آخرین آیتم در collection خروجی لحاظ می شود.

البته شما می توانید callback خود را به عنوان آرگومان ارسال کنید که خروجی آن کلیدی خواهد بود که مجموعه بر اساس آن کلیددهی (key) می شود: 

$keyed = $collection->keyBy(function ($item) {

    return strtoupper($item['product_id']);

});

$keyed->all();

/*

    [

        'PROD-100' => ['product_id' => 'prod-100', 'name' => 'Desk'],

        'PROD-200' => ['product_id' => 'prod-200', 'name' => 'Chair'],

    ]

*/

متد key()

متد keys تمامی کلیدهای یک مجموعه را برمی گرداند:

$collection = collect([

    'prod-100' => ['product_id' => 'prod-100', 'name' => 'Desk'],

    'prod-200' => ['product_id' => 'prod-200', 'name' => 'Chair'],

]);

$keys = $collection->keys();

$keys->all();

// ['prod-100', 'prod-200']

متد last()

آخرین المان موجود در یک collection را که با شرط مشخص شده در تابع callback منطبق می باشد را در خروجی برمی گرداند:

collect([1, 2, 3, 4])->last(function ($key, $value) {

    return $value < 3;

});

// 2

همچنین می توانید متد last را بدون هیچ آرگومان فراخوانی کنید که در آن صورت آخرین المان موجود در مجموعه را برمی گرداند. در صورت تهی بودن collection، مقدار null برگردانده می شود:

collect([1, 2, 3, 4])->last();

// 4

 

 

متد map()

متد map داخل مجموعه حلقه زده و سپس هر مقدار را به تابع callback پاس می دهد. callback می تواند آیتم را ویرایش نموده و آن را در خروجی برگرداند. بدین وسیله یک نمونه ی جدید از مجموعه، متشکل از آیتم های ویرایش شده ی collection اصلی در خروجی ارائه می شود: 

$collection = collect([1, 2, 3, 4, 5]);

$multiplied = $collection->map(function ($item, $key) {

    return $item * 2;

});

$multiplied->all();

// [2, 4, 6, 8, 10]

 

نکته: درست مانند بیشتر متدهای کلاس collection، تابع map یک نمونه ی جدید از کلاس مزبور را به عنوان خروجی برمی گرداند. بنابراین مجموعه ی اصلی که تابع بر روی آن اجرا شده دست نخورده باقی می ماند. به منظور تغییر collection اصلی بایستی متد transform را بر روی آن فراخونی نمایید.  

متد max()

این متد مقدار بیشینه ی کلید معین (بزرگترین مقدار عددی در مجموعه) را به عنوان خروجی برمی گرداند:

$max = collect([['foo' => 10], ['foo' => 20]])->max('foo');

// 20

$max = collect([1, 2, 3, 4, 5])->max();

// 5

متد merge()

متد merge آرایه ی ارسالی به عنوان پارامتر را با مجموعه ی مورد نظر ترکیب می کند. هر کلید رشته ای  موجود در آرایه که با کلید رشته ای داخل مجموعه منطبق باشد، جایگزین مقدار داخل مجموعه شده و روی مقدار آن بازنویسی می شود:  

$collection = collect(['product_id' => 1, 'name' => 'Desk']);

$merged = $collection->merge(['price' => 100, 'discount' => false]);

$merged->all();

// ['product_id' => 1, 'name' => 'Desk', 'price' => 100, 'discount' => false]

در صورتی که کلیدهای آرایه از نوع عددی باشند، مقادیر به انتهای مجموعه ضمیمه می شوند:

 

$collection = collect(['Desk', 'Chair']);

$merged = $collection->merge(['Bookcase', 'Door']);

$merged->all();

// ['Desk', 'Chair', 'Bookcase', 'Door']

تابع min()

متد min() کمترین مقدار را برمی گرداند:

$min = collect([['foo' => 10], ['foo' => 20]])->min('foo');

// 10

$min = collect([1, 2, 3, 4, 5])->min();

// 1

 

متد only()

این متد آیتم های موجود در مجموعه را که دارای کلید مشخص هستند به عنوان خروجی برمی گرداند:

$collection = collect(['product_id' => 1, 'name' => 'Desk', 'price' => 100, 'discount' => false]);

$filtered = $collection->only(['product_id', 'name']);

$filtered->all();

// ['product_id' => 1, 'name' => 'Desk']

متد only درست برعکس تابع except می باشد.

متد pluck()

متد pluck() تمامی مقادیر موجود در مجموعه که از نظر کلید ارسالی به آن یکسان هستند را در خروجی بازیابی می کند: 

$collection = collect([

    ['product_id' => 'prod-100', 'name' => 'Desk'],

    ['product_id' => 'prod-200', 'name' => 'Chair'],

]);

$plucked = $collection->pluck('name');

$plucked->all();

// ['Desk', 'Chair']

حتی می توانید با ارسال کلید به عنوان پارامتر دوم به این متد، مشخص نمایید collection خروجی با چه اسمی کلید دهی شود:

$plucked = $collection->pluck('name', 'product_id');

$plucked->all();

// ['prod-100' => 'Desk', 'prod-200' => 'Chair']

pop()

pop() آخرین آیتم را از مجموعه حذف کرده و در خروجی برمی گرداند:

$collection = collect([1, 2, 3, 4, 5]);

$collection->pop();

// 5

$collection->all();

// [1, 2, 3, 4]

 

متد prepend() یک آیتم را به ابتدای یک مجموعه اضافه می کند:

$collection = collect([1, 2, 3, 4, 5]);

$collection->prepend(0);

$collection->all();

// [0, 1, 2, 3, 4, 5]

در صورت تمایل می توانید یک آرگومان دوم به متد prepend() ارسال کنید که کلید آیتم ضمیمه شده به مجموعه را مشخص می کند:

$collection = collect(['one' => 1, 'two', => 2]);

$collection->prepend(0, 'zero');

$collection->all();

// ['zero' => 0, 'one' => 1, 'two', => 2]

 

pull()

این متد یک آیتم (مقدار) را بر اساس کلیدی که به عنوان آرگومان به آن ارسال شده از مجموعه حذف کرده و در خروجی برمی گرداند:

$collection = collect(['product_id' => 'prod-100', 'name' => 'Desk']);

$collection->pull('name');

// 'Desk'

$collection->all();

// ['product_id' => 'prod-100']

متد push()

این متد یک آیتم جدید را به انتهای مجموعه ی جاری اضافه می کند:

$collection = collect([1, 2, 3, 4]);

$collection->push(5);

$collection->all();

// [1, 2, 3, 4, 5]

متد put()

این متد یک جفت کلید مقدار جدید را که به صورت آرگومان به آن ارسال شده، در مجموعه درج می کند:

$collection = collect(['product_id' => 1, 'name' => 'Desk']);

$collection->put('price', 100);

$collection->all();

// ['product_id' => 1, 'name' => 'Desk', 'price' => 100]

random()

این متد یک آیتم را به صورت تصادفی از مجموعه گزینش نموده و در خروجی برمی گردارند:

$collection = collect([1, 2, 3, 4, 5]);

$collection->random();

// 4 - (retrieved randomly)

در صورت تمایل می توانید یک عدد صحیح را به عنوان آرگومان به متد ذکر شده پاس دهید. در صورتی که مقدار این آرگومان بزرگتر از 1 باشد، مجموعه ای از آیتم ها به صورت تصادفی در خروجی بازیابی می شود:

$random = $collection->random(3);

$random->all();

// [2, 4, 5] - (retrieved randomly)

متد reduce()

یک مجموعه از مقادیر را به تنها یک مقدار تبدیل می کند و نتیجه ی هر iteration را به iteration بعدی پاس می دهد: 

$collection = collect([1, 2, 3]);

$total = $collection->reduce(function ($carry, $item) {

    return $carry + $item;

});

// 6

مقدار متغیر $carry در تکرار یا چرخش اول null می باشد. البته شما می توانید با ارسال آرگومان دیگری به تابع مقدار آغازین را مشخص نمایید:

$collection->reduce(function ($carry, $item) {

    return $carry + $item;

}, 4);

// 10

 

تابع reject()

این متد مجموعه را بر اساس شرط تعیین شده در تابع callback که به عنوان پارامتر پاس داده شده فیلتر می کند. تابع callback بایستی برای هر آیتمی که با شرط منطبق نیستند و می خواهد از مجموعه ی ورودی حذف کند (و در مجموعه ی خروجی لحاظ نکند)، مقدار true را بر گرداند:

$collection = collect([1, 2, 3, 4]);

$filtered = $collection->reject(function ($value, $key) {

    return $value > 2;

});

$filtered->all();

// [1, 2]

متد reject عملکردی متضاد متد filter دارد.

متد reverse()

متد reverse() ترتیب قرارگیری آیتم های مجموعه را معکوس می کند:

$collection = collect([1, 2, 3, 4, 5]);

$reversed = $collection->reverse();

$reversed->all();

// [5, 4, 3, 2, 1]

 

متد search()

این متد مقداری که به عنوان آرگومان به آن پاس داده شده را داخل مجموعه جستجو کرده و کلید آن را در صورت یافتن مقدار مورد درخواست، در خروجی برمی گرداند. اگر آیتم مورد نظر یافت نشد، مقدار false برگردانده می شود:

$collection = collect([2, 4, 6, 8]);

$collection->search(4);

// 1

جستجو داخل مجموعه بر اساس یک مقایسه ی تقریبی صورت می پذیرد. به منظور اعمال مقایسه ی دقیق کافی است مقدار true را به عنوان پارامتر ورودی دوم به متد مذکور ارسال نمایید:

$collection->search('4', true);

// false

همچنین می توانید تابع بازفرخوان (callback) خود را به عنوان آرگومان ارسال نمایید. این تابع به دنبال اولین آیتمی (از نظر مکان قرارگیری) که با شرط مشخص شده داخلش مطابقت دارد گشته و آن را به عنوان خروجی برمی گرداند:

$collection->search(function ($item, $key) {

    return $item > 5;

});

// 2

 

متد shift()

اولین آیتم را از مجموعه حذف کرده و در خروجی بازمی گرداند:

$collection = collect([1, 2, 3, 4, 5]);

$collection->shift();

// 1

$collection->all();

// [2, 3, 4, 5]

تابع shuffle()

آیتم های موجود در مجموعه را به طور تصادفی (و نه بر اساس ترتیب مشخص) مرتب می کند:

$collection = collect([1, 2, 3, 4, 5]);

$shuffled = $collection->shuffle();

$shuffled->all();

// [3, 2, 5, 1, 4] // (generated randomly)

 

متد slice()

این متد بر اساس اندیس شروع که به عنوان آرگومان به آن ارسال شده یک مجموعه را برش داده و در خروجی برمی گرداند. مثال زیر آیتم های بعد از شماره ی مکان قرارگیری 4 را از مجموعه استخراج می کند:

$collection = collect([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);

$slice = $collection->slice(4);

$slice->all();

// [5, 6, 7, 8, 9, 10]

 برای کاهش سایز خروجی (و محدود تر کردن نتیجه)، می توانید یک آرگومان دوم به متد ارسال کنید. این آرگومان تعداد آیتم های استخراجی از collection را تعیین می کند:

$slice = $collection->slice(4, 2);

$slice->all();

// [5, 6]

آیتم های استخراج شده به عنوان خروجی مجددا با کلیدهای عددی اندیس گذاری می شوند (کلیدهایی از نوع عدد اندیس جدید آن ها خواهد بود). اگر مایلید کلیدهای اصلی را حفظ کنید، درآن صورت کافی است مقدار true را به عنوان آرگومان سوم به متد ارسال نمایید.

sort()

این متد مجموعه را مرتب سازی می نماید:

$collection = collect([5, 3, 1, 2, 4]);

$sorted = $collection->sort();

$sorted->values()->all();

// [1, 2, 3, 4, 5]

مجموعه ی مرتب شده کلیدهای اصلی آرایه را حفظ می کند (کلیدهای اصلی آرایه پس از مرتب سازی تغییر نکرده و ثابت می ماند). در این مثال با استفاده از متد values کلید های آیتم های مجموعه را دوباره (به ترتیب عددی متوالی) اندیس گذاری می کنیم.

به منظور مرتب سازی مجموعه ای از اشیا و آرایه های تودرتو، بخش های مربوط به متدهای sortBy و sortByDesc را مطالعه نمایید.

در صورتی که مرتب سازی شما حالت پیچیده تری به خود می گیرد، درآن صورت می توانید یک تابع callback با الگوریتم اختصاصی خود به عنوان آرگومان به متد sort ارسال کنید. 

متد sortBy()

این متد مجموعه را بر اساس کلید ارسالی (به عنوان آرگومان) مرتب سازی می کند:

$collection = collect([

    ['name' => 'Desk', 'price' => 200],

    ['name' => 'Chair', 'price' => 100],

    ['name' => 'Bookcase', 'price' => 150],

]);

$sorted = $collection->sortBy('price');

$sorted->values()->all();

/*

    [

        ['name' => 'Chair', 'price' => 100],

        ['name' => 'Bookcase', 'price' => 150],

        ['name' => 'Desk', 'price' => 200],

    ]

*/

کلیدهای آرایه پس از مرتب سازی ثابت می ماند. در این نمونه، از متد values برای اندیس گذاری آیتم های آرایه به ترتیب عددی متوالی بهره گرفتیم (با values کلیدهای آرایه را به ترتیب عددی متوالی اندیس گذاری می کنیم).

شما می توانید تابع callback خود را برای تعیین نحوه ی مرتب سازی مقادیر مجموعه به متد نام برده ارسال کنید:

$collection = collect([

    ['name' => 'Desk', 'colors' => ['Black', 'Mahogany']],

    ['name' => 'Chair', 'colors' => ['Black']],

    ['name' => 'Bookcase', 'colors' => ['Red', 'Beige', 'Brown']],

]);

$sorted = $collection->sortBy(function ($product, $key) {

    return count($product['colors']);

});

$sorted->values()->all();

/*

    [

        ['name' => 'Chair', 'colors' => ['Black']],

        ['name' => 'Desk', 'colors' => ['Black', 'Mahogany']],

        ['name' => 'Bookcase', 'colors' => ['Red', 'Beige', 'Brown']],

    ]

*/

متد sortByDesc()

این متد از نظر نوع و تعداد پارامتر ورودی (signature) با متد sortBy یکسان است ولی مجموعه ی مورد نظر را بجای صعودی، به ترتیب نزولی sort می کند.

متد splice()

متد splice یک اندیس شروع به عنوان آرگومان می پذیرد. سپس آیتم هایی که پس از اندیس مشخص شده قرار دارند را از مجموعه برش داده و استخراج می کند:

$collection = collect([1, 2, 3, 4, 5]);

$chunk = $collection->splice(2);

$chunk->all();

// [3, 4, 5]

$collection->all();

// [1, 2]

می توانید یک آرگومان دوم پاس دهید که تعداد آیتم های استخراجی از مجموعه را مشخص می کند. از این طریق می توانید اندازه ی خروجی را محدود نمایید:

$collection = collect([1, 2, 3, 4, 5]);

$chunk = $collection->splice(2, 1);

$chunk->all();

// [3]

$collection->all();

// [1, 2, 4, 5]

علاوه بر دو آرگومان اول، می توان یک آرگومان سوم حاوی مقادیری که قرار است جایگزین مقادیر حذف شده از مجموعه گردد به متد ارسال کنید:

$collection = collect([1, 2, 3, 4, 5]);

$chunk = $collection->splice(2, 1, [10, 11]);

$chunk->all();

// [3]

$collection->all();

// [1, 2, 10, 11, 4, 5]

متد sum()

این متد حاصل جمع تمامی آیتم های موجود در مجموعه را به عنوان خروجی برمی گرداند:

collect([1, 2, 3, 4, 5])->sum();

// 15

چنانچه مجموعه دربردارنده ی آرایه ها یا اشیا تودرتو باشد، در آن صورت می بایست یک کلید به عنوان آرگومان ارسال کنید. این آرگومان مشخص می کند حاصل جمع کدام مقادیر بایستی به عنوان خروجی برگردانده شوند:

$collection = collect([

    ['name' => 'JavaScript: The Good Parts', 'pages' => 176],

    ['name' => 'JavaScript: The Definitive Guide', 'pages' => 1096],

]);

$collection->sum('pages');

// 1272

علاوه بر آن می توانید یک تابع callback به متد ارسال کنید که مشخص می کند حاصل جمع کدام مقادیر از مجموعه بایستی محاسبه شده و در خروجی لحاظ شود:

$collection = collect([

    ['name' => 'Chair', 'colors' => ['Black']],

    ['name' => 'Desk', 'colors' => ['Black', 'Mahogany']],

    ['name' => 'Bookcase', 'colors' => ['Red', 'Beige', 'Brown']],

]);

$collection->sum(function ($product) {

    return count($product['colors']);

});

// 6

متد take()

این متد یک مجموعه ی جدید از collection جاری ایجاد کرده و در خروجی برمی گرداند. پارامتر ارسالی به متد تعداد آیتم هایی که باید در خروجی لحاظ شود را مشخص می کند:

$collection = collect([0, 1, 2, 3, 4, 5]);

$chunk = $collection->take(3);

$chunk->all();

// [0, 1, 2]

همچنین می توانید یک عدد صحیح منفی به تابع ارسال نموده و آیتم های مورد نظر را از انتهای مجموعه استخراج نمایید:

$collection = collect([0, 1, 2, 3, 4, 5]);

$chunk = $collection->take(-2);

$chunk->all();

// [4, 5]

متد toArray()

این متد همان طور که از نامش پیدا است collection مورد نظر را به یک آرایه ی متعارف در PHP تبدیل می کند. چنانچه مقادیر موجود در مجموعه از مدل های Eloquent باشند، در آن صورت مدل ها نیز در اثر اجرای این متد به آرایه تبدیل می شوند: 

 

$collection = collect(['name' => 'Desk', 'price' => 200]);

$collection->toArray();

/*

    [

        ['name' => 'Desk', 'price' => 200],

    ]

*/

 

نکته: toArray تمامی اشیای قرار گرفته در دل مجموعه (nested و تودرتو) را نیز به آرایه تبدیل می کند. اگر می خواهید آرایه را همان طور که هست (بدون تغییر) دریافت کنید، در آن صورت بایستی (بجای متد نام برده) متد all را فراخوانی نمایید. 

تابع toJson()

مجموعه را به فرمت JSON تبدیل می کند:

$collection = collect(['name' => 'Desk', 'price' => 200]);

$collection->toJson();

// '{"name":"Desk","price":200}'

متد transform()

این متد داخل مجموعه حلقه زده و callback ارسالی به متد را به ازای هر آیتم داخل collection صدا می زند. آیتم های موجود در مجموعه با مقادیر بازگشتی (خروجی callback) جایگزین می شوند:  

$collection = collect([1, 2, 3, 4, 5]);

$collection->transform(function ($item, $key) {

    return $item * 2;

});

$collection->all();

// [2, 4, 6, 8, 10]

 

نکته: بر خلاف عمده ی متدهای کلاس collection، تابع transforms در اصل خود مجموعه تغییرات را اعمال نموده و یک مجموعه ی ویرایش شده را در نتیجه بر می گرداند. اگر می خواهید یک مجموعه ی جدید بجای ویرایش مجموعه ی اصلی دریافت کنید، در آن صورت بایستی متد map را فراخوانی نمایید.  

 

 

متد union()

تابع union آرایه ی ورودی را به مجموعه ی جاری اضافه می کند. در صورتی که آرایه ی ورودی دربردارنده ی کلیدهایی باشد که مانند آن ها در مجموعه موجود است، مقادیر مجموعه ارجحیت پیدا می کنند:

$collection = collect([1 => ['a'], 2 => ['b']]);

$union = $collection->union([3 => ['c'], 1 => ['b']]);

$union->all();

// [1 => ['a'], 2 => ['b'], [3 => ['c']]

 

متد unique()

متد unique تمامی آیتم های یگانه و منحصربفرد (غیرتکراری) داخل مجموعه را در خروجی برمی گرداند:

$collection = collect([1, 1, 2, 2, 3, 4, 2]);

$unique = $collection->unique();

$unique->values()->all();

// [1, 2, 3, 4]

کلید های آرایه در مجموعه ی خروجی تغییر نخواهند کرد. در این مثال با استفاده از متد values مجددا آیتم های موجود در آرایه را به ترتیب عددی متوالی اندیس گذاری کردیم. 

برای آرایه ها و اشیا تودرتو نیز بایستی یک کلید به عنوان آرگومان که آیتم منحصربفرد را مشخص می کند به تابع ارسال کنید:

$collection = collect([

    ['name' => 'iPhone 6', 'brand' => 'Apple', 'type' => 'phone'],

    ['name' => 'iPhone 5', 'brand' => 'Apple', 'type' => 'phone'],

    ['name' => 'Apple Watch', 'brand' => 'Apple', 'type' => 'watch'],

    ['name' => 'Galaxy S6', 'brand' => 'Samsung', 'type' => 'phone'],

    ['name' => 'Galaxy Gear', 'brand' => 'Samsung', 'type' => 'watch'],

]);

$unique = $collection->unique('brand');

$unique->values()->all();

/*

    [

        ['name' => 'iPhone 6', 'brand' => 'Apple', 'type' => 'phone'],

        ['name' => 'Galaxy S6', 'brand' => 'Samsung', 'type' => 'phone'],

    ]

*/

همچنین می توانید callback خود را به متد unique ارسال کرده و از این طریق آیتم منحصربفرد و غیر تکراری را در خروجی برگردانید:

$unique = $collection->unique(function ($item) {

    return $item['brand'].$item['type'];

});

$unique->values()->all();

/*

    [

        ['name' => 'iPhone 6', 'brand' => 'Apple', 'type' => 'phone'],

        ['name' => 'Apple Watch', 'brand' => 'Apple', 'type' => 'watch'],

        ['name' => 'Galaxy S6', 'brand' => 'Samsung', 'type' => 'phone'],

        ['name' => 'Galaxy Gear', 'brand' => 'Samsung', 'type' => 'watch'],

    ]

*/

 

متد values()

این متد یک مجموعه ی جدید را به عنوان خروجی برگردانده و کلیدهای آن را به اعداد صحیح متوالی ریست می کند: 

$collection = collect([

    10 => ['product' => 'Desk', 'price' => 200],

    11 => ['product' => 'Desk', 'price' => 200]

]);

$values = $collection->values();

$values->all();

/*

    [

        0 => ['product' => 'Desk', 'price' => 200],

        1 => ['product' => 'Desk', 'price' => 200],

    ]

*/

متد where()

این متد یک مجموعه را بر اساس جفت/کلید مقدار (key/value pair) ارسالی فیلتر می کند:

$collection = collect([

    ['product' => 'Desk', 'price' => 200],

    ['product' => 'Chair', 'price' => 100],

    ['product' => 'Bookcase', 'price' => 150],

    ['product' => 'Door', 'price' => 100],

]);

$filtered = $collection->where('price', 100);

$filtered->all();

/*

[

    ['product' => 'Chair', 'price' => 100],

    ['product' => 'Door', 'price' => 100],

]

*/

متد where مقادیر آیتم های یک مجموعه را بر اساس مقایسه و تطبیق دقیق فیلتر می کند. برای فیلتر کردن خروجی بر اساس مقایسه و تطبیق تقریبی (loose comparison) بایستی متد whereloose را فراخوانی نمایید.

متد whereLoose()  

whereLoose() از نظر تعداد و نوع پارامتر ورودی (signature) با متد قبلی تفاوتی ندارد به جز اینکه در این متد تمامی مقادیر بر اساس مقایسه ی منطقی تقریبی تطبیق و فیلتر می شوند.

متد whereIn()

این متد بر اساس جفت کلید و مقدار که به عنوان آرگومان به آن فرستاده می شود، مجموعه را فیلتر می کند:

$collection = collect([

    ['product' => 'Desk', 'price' => 200],

    ['product' => 'Chair', 'price' => 100],

    ['product' => 'Bookcase', 'price' => 150],

    ['product' => 'Door', 'price' => 100],

]);

$filtered = $collection->whereIn('price', [150, 200]);

$filtered->all();

/*

[

    ['product' => 'Bookcase', 'price' => 150],

    ['product' => 'Desk', 'price' => 200],

]

*/

متد whereIn() مقادیر آیتم ها را بر اساس تطبیق منطقی دقیق بررسی و مقایسه می کند.

متد whereInLoose()

این متد از نظر تعداد و نوع پارامتر ورودی (signature) با متد wherein فرق ندارد و تنها تفاوت آن ها در نحوه ی تطبیق مقادیر و فیلتر کردن نتیجه می باشد (whereInLoose از تطبیق منطقی تقریبی و غیر دقیق برای مقایسه و فیلتر نتیجه بهره می گیرد).

متد zip()

متد zip() مقادیر آرایه ی ورودی را با مقادیر موجود در مجموعه در اندیس منطبق (که دارای شماره ی مکان قرارگیری یکسان است) ترکیب می کند:

$collection = collect(['Chair', 'Desk']);

$zipped = $collection->zip([100, 200]);

$zipped->all();

// [['Chair', 100], ['Desk', 200]]