گرد آورندگان :
آقایان رضا طاهری
و محمدصالح ساغرچی
منبع : https://www.raywenderlich.com/8003281-core-graphics-tutorial-getting-started
شما تازه نوشتن یک برنامه را تمام کرده اید. برنامه ی شما به خوبی کار می کند ولی رابط کاربری آن از هیچ گونه ظرافتی برخوردار نیست. شما می توانید با طراحی چند تصویر در فوتوشاپ آن را بهبود بدهید و دعا کنید که اپل صفحه نمایشی با رزولوشن 4 برابر عرضه نکند. این کار زمان بر است و از استراتژی خاصی بهره نمی برد. از سوی دیگر شما می توانید از Core Graphics برای طراحی تصاویری استفاده کنید که متناسب با هر صفحه ای تغییر اندازه می دهند.
در واقع Core Graphics، چارچوبی برای طراحی تصاویر vector است. این ابزار، یک API بسیار بزرگ و قدرتمند است که قابلیت های زیادی را از جمله @IBDesignable و @IBInspectable در اختیار ما می گذارد .
فرض کنید یک پزشک به شما پیشنهاد می دهد که روزی هشت لیوان آب مصرف کنید. ممکن است فکر کنید مشکلی برای انجام اینکار وجود ندارد ولی بعد از چند روز متوجه می شوید که چقدر راحت حساب کار از دستتان در می رود؛ از خودتان می پرسید که این بعد از ظهر سه لیوان آب نوشیده اید یا دو لیوان، یا مثلا شک می کنید که بطری آب روی میزتان را دیروز تمام کرده اید یا پریروز.
در این آموزش، شما برنامه ای را توسعه خواهید داد که عادت های نوشیدن آب کاربران را رهگیری می کند. با استفاده از این برنامه، هر گاه که شما یک لیوان آب خنک نوش جان می کنید، بر روی یک دکمه ی شمارنده ضربه می زنید. همزمان که داده ها شروع به تجمیع می کنند، این برنامه یک نمودار برای نشان دادن مصرف آب هفتگی شما درست می کند.
اسم این برنامه را Flo می گذاریم و ظاهر کامل آن به شکل زیر خواهد بود:
نکته: این آموزش، بخش اول از یک آموزش سه قسمتی است. در بخش اول شما با استفاده از روش های طراحی در UIKit اقدام به ساخت سه کلاس جدید می کنید. در بخش دوم شما در بخش context ها در Core Graphics عمیق تر می شوید و نموداری برای نشان دادن روند روزانه ی مصرف آب طراحی می کنید. در قسمت سوم، شما پس زمینه ی طرح دار برنامه را طراحی می کنید و کار نوشتن برنامه را تمام می کنید. در آخر هم خودتان به خودتان یک مدال برای یادگیری Core Graphicsجایزه می دهید ?
فایل های مورد نیاز ساخت پروژه را با کلیک کردن برروی دکمه ی Download Materials در بالا و یا پایین صفحه در آدرس https://www.raywenderlich.com/8003281-core-graphics-tutorial-getting-started دریافت کنید. Starter project را در Xcode باز کنید. پس اجرای این پروژه، چیزی مانند زیر خواهید دید:
شما حالا پروژه ای شامل یک storyboard و یک view controller در اختیار دارید. ساختن مابقی آن بر عهده ی شماست!
سه مرحله برای ساخت یک تصویر باید دنبال شود:
شما این مراحل را با طراحی یک دکمه ی + امتحان خواهید کرد. این دکمه در نهایت شبیه تصویر زیر خواهد شد:
ابتدا باید یک فایل جدید بسازید. به مسیر File -> New -> File… بروید ، iOS -> Source -> Cocoa -> Touch Class را انتخاب کنید و بر روی دکمه ی Next کلیک کنید.
در این صفحه نام کلاس جدید را PushButton بگذارید و آن را تبدیل به یک زیرکلاس از UIButton کنید. مطمئن شوید که زبان انتخابی شما Swift است و سپس به ترتیب بر روی دکمه ها ی Next و Create کلیک کنید.
نکته: از آنجایی که UIButtonیک زیرکلاس از UIViewاست، همه ی توابع موجود در UIViewاز جمله draw(_:) در UIButton نیز وجود دارد.
در Main.storyboard یک UIButton را به داخل view controller بکشید و آن را در Document Outline انتخاب کنید.
در بخش Identity inspector کلاس آن را به PushButton تغییر دهید:
در اینجا شما محدودیت های Auto Layout را تنظیم خواهید کرد:
نکته: عملیات control-drag به این صورت است که شما همزمان با نگه داشتن دکمه ی control، کلیک چپ را فشار داده و نگه می دارید؛ سپس با جابجا کردن نشانگر ماوس، عملیات control-drag صورت می پذیرد.
این کار ها باعث می شود که چهار محدودیت مورد نیاز برای Auto Layout تنظیم شود. شما می توانید آنها را در بخش Size inspector مشاهده کنید:
بر روی گزینه ی Edit در قسمت Align Center Y to کلیک کنید و آن را برابر با مقدار ثابت 100 قرار دهید. این تغییر باعث می شود تا مکان عمودی دکمه 100 نقطه پایین تر از وسط صفحه باشد. همچنین، مقادیر Width و Height را برابر با مقدار ثابت 100 قرار دهید. محدودیت های نهایی باید مانند زیر باشند:
در بخش Attributes inspector، مقدار پیش فرض Default Title را حذف کنید :
می توانید برنامه ی خود را همین الان اجرا کنید، ولی اگر اینکار را بکنید یک صفحه ی خالی خواهید دید؛ وقت آن است که این مشکل را برطرف کنیم!
یاد آوری می کنیم که دکمه ای که به دنبال طراحی آن هستیم به شکل دایره است:
برای کشیدن اشکال مختلف در Core Graphics، شما باید یک "مسیر" مشخص کنید که به Core Graphics می گوید چه خط و یا منحنی ای را برای انجام عملیات های گرافیکی در نظر بگیرد؛ مثلا وقتی که می خواهیم دو خط صاف بکشیم تا علامت + را طراحی کنیم، باید دو مسیر را مشخص کنیم تا نقاط موجود در این مسیر ها به شکل مورد نیاز ما رنگ آمیزی شوند و یا وقتی که می خواهیم دایره ی زمینه ی دکمه را طراحی کنیم، باید مسیری به شکل دایره مشخص کنیم و به Core Graphics بگوییم که درون این مسیر را رنگ آمیزی کند.
سه مفهوم پایه درباره ی مسیرها وجود دارد:
یک راه ساده برای ساختن مسیر ها در Core Graphics استفاده از یک کلاس پر کاربرد به نام UIBezierPath است. این کلاس به ما اجازه می دهد که با استفاده از یک API ساده اقدام به تولید مسیر های مختلف کنیم. این مسیر ها می توانند بر پایه ی خطوط، منحنی ها، چهارضلعی ها و یا دنباله ای از نقاط تعریف شوند.
در ابتدای کار، از UIBezierPath برای ساختن یک مسیر بسته و پر کردن آن با رنگ سبز استفاده می کنیم. PushButton.swift را باز کنید و تابع زیر را به آن اضافه کنید:
شما بعد از اضافه کردن این قطعه کد، یک بیضی به اندازه ی چهارضلعی ای که به آن پاس داده شده است طراحی کرده اید. در این حالت خاص، این اندازه برابر با اندازه ی دکمه ی شما خواهد بود که آن را در بخش storyboard برابر با 100x100 تعیین کرده بودید؛ به همین دلیل این بیضی به شکل دایره در می آید.
از آنجا که مسیر ها خودشان چیزی را بر روی صفحه نمی کشند، شما می توانید آنها را، بدون اینکه نیازی به context برای کشیده شدن داشته باشید، تعریف کنید. برای رنگ کردن نقاط درون یک مسیر، باید رنگ fill را برروی context فعلی تعیین کنید و سپس مسیر را پر کنید. جلوتر درباره ی این موضوع بیشتر صحبت خواهیم کرد.
اگر برنامه را اجرا کنید، یک دایره ی سبز رنگ مشاهده خواهید کرد:
تا اینجا، شما یاد گرفته اید که چگونه می شود view هایی با طراحی دلخواه پیاده سازی کرد. شما این کار را با ساختن یک UIButton، نوشتن کد برای تابع draw(_ :) و اضافه کردن این UIButton به storyboard انجام داده اید.
هر UIView دارای یک context گرافیکی است. همه ی طراحی های موجود برای یک view، پیش از آنکه به بخش سخت افزاری فرستاده شوند، درون این context رندر می شوند.
سیستم عامل iOS هر وقت که نیاز به بروز رسانی یک view داشته باشد، با صدا کردن تابع draw(_ :) این کار را انجام می دهد. صدا کردن تابع draw(_ :) وقتی رخ می دهد که حداقل یکی از شرایط زیر فراهم شده باشد :
1. عنصر View به تازگی در صفحه نمایان شده باشد
2. بقیه ی view هایی که روی view ما قرار داشته اند حرکت کرده باشند
3. مقدار متغیر hidden تغییر کرده باشد
4. صریحا تابع setNeedsDisplay و یا setNeedsDisplayInRect را بر روی یک view صدا کرده باشید
نکته: هر طراحی ای که در تابع draw(_ :)اتفاق بیافتد وارد contextمربوط به همان viewمی شود. حواستان باشد که اگر خارج از این تابع اقدام به طراحی کنید، باعث به وجود آمدن یک context جدید می شوید.
شما تا اینجای آموزش هنوز از Core Graphics استفاده نکرده اید، چرا که UIKit خودش شامل wrapper های زیادی برای عملکرد های متنوع Core Graphics است. به عنوان مثال کلاس UIBezierPath خودش یک wrapper برای کلاس CGMutablePath است که از جمله API های سطح پایین تر Core Graphics به حساب می آید.
نکته: هیچ وقت تابع draw(_ :)را به صورت مستقیم صدا نزنید. اگر می بینید که view طراحی شده توسط شما بروز رسانی نمی شود، تابع setNeedsDisplay را صدا بزنید.
تابع setNeedsDisplay خودش تابع draw(_ :)را صدا نمی زند بلکه viewرا به عنوان dirtyعلامت می زند که باعث می شود در سیکل بعدی بروز رسانی صفحه، تابع draw(_ :) صدا زده شود. حتی اگر شما تابع setNeedsDisplay را پنج بار پشت سر هم بنویسید و صدا بزنید، تابع draw(_ :) فقط یکبار فراخوانی خواهد شد.
نوشتن کد برای طراحی یک مسیر و سپس اجرای برنامه برای دیدن آن، فقط در حد یک برنامه آموزشی سال 1920 حذابیت دارد. خوشبختانه ما گزینه های بیشتری در اختیار داریم.
عملیات Live Rendering به view ها اجازه می دهد که با دقت بالا بر روی storyboard نمایان شوند. نکته ی مهمتر این است که storyboard بلافاصله خودش را بروز رسانی می کند تا با تغییرات ناشی از اجرای تابع draw(_ :) وفق پیدا کند، تنها کاری که ما باید انجام دهیم صرفا استفاده از یک خصیصه ی ساده است!
خط زیر را درست پیش از خط تعریف کلاس در فایل PushButton.swift قرار دهید:
این خط باعث فعال شدن قابلیت Live Rendering می شود. اگر به Main.storyboard بازگردید می بینید که دکمه ی شما به صورت یک دایره ی سبز رنگ در آمده است درست مانند وقتی که برنامه را بر روی گوشی خود اجرا می کردید.
پس از این، وقت آن است که صفحه ی خود را به گونه ای تنظیم کنید که کد و storyboard به طور همزمان کنار یکدیگر دیده شوند.
برای اینکار ابتدا فایل PushButton.swift را انتخاب کنید تا کد شما نمایش داده شود، سپس با نگه داشتن دکمه ی Option و کلیک بر روی Main.storyboard می توانید هر دو فایل را کنار یکدیگر مشاهده کنید:
حال بخش document outline را ببندید؛ این کار را یا با کشیدن گوشه ی بخش document outline و یا با کلیک بر روی دکمه ای که در پایین storyboard قرار دارد انجام دهید:
وقتی که کارتان تمام شد، صفحه ی شما باید مانند زیر باشد:
در تابع draw(_ :)، کد زیر را بیابید:
آن را مانند زیر تغییر دهید:
در storyboard، می توانید مشاهده کنید که رنگ دکمه از سبز به آبی تغییر پیدا می کند. خیلی جذاب است!
حالا آمادگی این را داریم که خطوط مربوط به علامت + را طراحی کنیم.
Core Graphics از "painter's model" بهره می برد. وقتی که شما چیزی را درون یک context طراحی می کنید، کار شما تقریبا مانند نقاشی کردن است؛ یک مسیر را تعریف می کنید و آن را رنگ آمیزی می کنید، سپس یک مسیر دیگر را تعریف می کنید و درون آن را با رنگ پر می کنید. شما نمی توانید تک تک پیکسل هایی که از قبل رنگ شده اند را تغییر دهید اما می توانید روی آنها رنگ جدید بکشید. تصویر زیر که برگرفته از اسناد شرکت Apple است نشان می دهد که این روند به چه صورت انجام می پذیرد. درست مانند نقاشی کردن بر روی بوم، ترتیب کشیدن مسیر ها و رنگ ها از اهمیت بالایی برخوردار است.
علامت + ای که شما قرار است بسازید، بر روی دایره ی آبی دکمه قرار می گیرد؛ پس شما باید کد مربوط به طراحی دایره ی آبی را قبل از کد مربوط به علامت + بنویسید. شما می توانید از دو چهار ضلعی برای ساختن علامت + استفاده کنید ولی کار راحت تر این است که دو مسیر طراحی کرده و خود این مسیر ها را با استفاده از عملیات stroke و با ضخامت دلخواه رنگ آمیزی کنید.
Struct و مقادیر ثابت زیر را به PushButton اضافه کنید:
سپس کد زیر را به انتهای تابع draw(_ :) اضافه کنید تا خط افقی مربوط به علامت + تولید شود:
در اینجا، شما از UIBezierPath برای طراحی خط افقی بهره برده اید. به این صورت که نقطه ای برای شروع در سمت چپ دایره به آن داده اید و مسیر را تا سمت راست دایره ادامه داده اید. در نهایت هم مسیر را با استفاده از stroke به رنگ سفید رنگ آمیزی کرده اید.
حال در storyboard شما، یک دایره ی آبی رنگ به همراه یک خط افقی دیده می شود:
نکته: حواستان باشد که یک مسیر صرفا مجموعه ای از نقاط است. یک راه ساده برای درک این مفهوم این است که خودکاری را در دست خود تجسم کنید و بر روی یک صفحه، دو نقطه را علامت بزنید؛ سپس خودکار را بر روی نقطه ی شروع گذاشته و آن را با استفاده از یک خط به نقطه ی پایان متصل کنید. این کار دقیقا مانند همان کاری است که شما با استفاده از تابع move(to:) و تابع addLine(to:) انجام دادید
اگر برنامه را بر روی یک شبیه ساز iPad 2 و یا iPhone 8 Plus اجرا کنید، مشاهده می کنید که خط سفیدی که طراحی کرده بودید در آن حدی که باید واضح باشد واضح نیست و یک خط آبی کمرنگ آن را احاطه کرده است.
مشکل کجاست؟
در روز هایی که اولین iPhone به بازار آمده بود، نقاط و پیکسل ها به یک اندازه بودند و فضای یکسانی را اشغال می کردند. این کار باعث می شد که این دو مفهوم، عملا یکسان باشند. بعدها iPhone هایی که با صفحه نمایش retina به بازار آمدند دارای چهار برابر پیکسل بیشتر نسبت به قبل بودند در حالی که تعداد نقاط برایشان تغییری نکرده بود. اخیرا هم، iPhone 8 Plus دوباره تعداد پیکسل ها را افزایش داده در حالی که تعداد نقاط تغییری نکرده.
نکته: آنچه که در ادامه می آید یک تعبیر مفهومی است و پیکسل ها در سطح سخت افزار واقعی ممکن است فرق داشته باشد. به عنوان مثال، در iPhone 8 Plus عملیات downsampling انجام می شود تا تصویر مورد نظر به صورت کامل در صفحه ی گوشی نمایش داده شود. برای کسب دانش بیشتر درباره ی downsampling به این لینک مراجه کنید:
http://www.paintcodeapp.com/news/iphone-6-screens-demystified
در تصویر زیر، یک آرایه ی 12x12 از پیکسل ها مشاهده می کنید که نقاط در آنها با رنگ خاکستری مشخص شده اند. صفحه نمایش iPad 2 دارای یک نگاشت یک به یک از پیکسل ها به نقاط است و به همین دلیل آنرا 1x می نامند. iPhone 8 دارای یک صفحه نمایش 2x است یعنی چهار پیکسل برای هر نقطه تخصیص داده شده است. در نهایت هم iPhone 8 Plus یک صفحه نمایش 3x است و 9 پیکسل به هر نقطه تخصیص یافته است.
خطی که شما به عنوان خط افقی علامت + کشیدید، دارای ضخامتی به مقدار 3 نقطه است و از آنجا که عملیات storke از وسط هر مسیر به دو طرف انجام می شود، می توانیم بگوییم که از هر طرف به مقدار 1.5 نقطه به رنگ سفید رنگ آمیزی خواهد شد.
تصویر زیر، نشان می دهد که چگونه یک خط به ضخامت 3 نقطه در هر کدام از انواع صفحه ها نمایش داده می شود. می توانید مشاهده کنید که در صفحات 1x و 3x خط ما در بعضی جاها فقط نصف یک پیکسل را اشغال کرده است که بدیهتا به این صورت قابل نمایش نخواهد بود. برای رفع این مشکل، iOS از عملیات anti aliasing استفاده می کند تا پیکسل های نصفه را با رنگی میان دو رنگ مرزی رنگ کند و این باعث می شود که خط نهایی وضوح خود را از دست بدهد.
در واقعیت، صفحه نمایش های 3x آنقدر پیکسل های زیادی دارند که شما احتمالا این نا واضحی را احساس نخواهید کرد. ولی اگر شما بر روی یک صفحه ی غیر retina مانند iPad 2 به توسعه ی نرم افزار م پردازید، باید تلاش خود را بکنید تا از وقوع anti aliasing جلوگیری کنید.
اگر شما خطوطی را طراحی می کنید که دارای طول و یا ضخامت فرد هستند، باید آنها را 0.5 نقطه با بالا و یا به پایین انتقال دهید تا از anti aliasing جلوگیری کنید. اگر به تصویر قبل نگاه کنید، مشاهده می کنید که انتقال به میزان 0.5 نقطه در صفحات 1x باعث انتقال به میزان 0.5 پیکسل می شود. 0.5 نقطه در صفحات 2x باعث انتقال به اندازه ی یک پیکسل کامل شده و در صفحات 3x باعث انتقال به اندازه ی یک و نیم پیکسل می شود.
در تابع draw(_ :)، توابع move(to:) و addLine(to:) را با کد زیر جایگزین کنید:
از آنجا که شما مسیر را به میزان 0.5 نقطه انتقال می دهید، iOS حالا خط مورد نظر را بر روی همه ی صفحه نمایش ها به صورت کاملا واضح نمایش می دهد.
نکته: برای داشتن خطوطی که کاملا یک پیکسل را پر می کنند، می توانید از UIBezierPath(rect:) بهره بجویید و سپس از contentScaleFactor برای محاسبه ی عرض و ارتفاع چهار ضلعی خود استفاده کنید. بر خلاف strokeها که از وسط مسیر به دو طرف رنگ آمیزی را انجام می دهند، fill ها فقط داخل یک مسیر را پر می کنند.
بعد از اضافه کردن کد قبلی، وقت این است که کد مربوط به خط عمودی علامت + را به تابع draw(_:) اضافه کنیم که البته باید حواسمان باشد که این کد را پیش از خط مربوط تعیین رنگ stroke اضافه کنیم:
همانطور که مشاهده می کنید، این کد تقریبا عین وقتی است که می خواستید خط افقی را به دکمه ی خود اضافه کنید.
حالا باید بتوانید نتیجه ی نهایی را در storyboard مشاهده کنید و هم اکنون، طراحی علامت + برای دکمه ی ما به پایان رسیده است:
ممکن است مواقعی پیش بیاید که شما بر روی یک دکمه بیش از یک بار کلیک کنید تا از عمل کردن آن اطمینان حاصل کنید. به عنوان یک توسعه دهنده، شما باید راهکار هایی ارائه دهید که اگر کاربر بیش از مقدار مورد نیاز بر روی دکمه ی + کلیک کرد، توانایی اصلاح عمل خود را داشته باشد. برای این منظور باید یک دکمه ی کم کردن با علامت - نیز به برنامه اضافه شود.
دکمه ی - درست مانند دکمه ی + خواهد بود با این فرق که خط عمودی را به آن اضافه نمی کنیم و رنگ دایره ی را متفاوت انتخاب می کنیم. شما از همان کلاس PushButton برای دکمه ی - استفاده خواهید کرد و حال باید قابلیتی داشته باشیم که بشود نوع و رنگ آن را پیش از اضافه کردن آن به storyboard تعیین کنیم.
@IBInspectable خاصیتی است که می توان آن را به یک متغیر اضافه کرد تا این متغیر توسط XCode قابل خواندن و تغییر دادن باشد. این یعنی شما می توانید این متغیر ها را در storyboard تنظیم کنید و نیازی به استفاده از کد زدن نخواهید داشت.
در بالای PushButton دو متغیر زیر را به همراه @IBInspectable اضافه کنید:
نکته: برای استفاده از @IBInspectableباید حتما تایپ متغیر ها را تعیین کنید وگرنه XCode برای تعیین کردن تایپ متغیر ها به مشکل می خورد.
خط زیر را در تابع draw(_ :) پیدا کنید:
آن را به شکل زیر تغییر دهید:
دکمه ی شما به رنگ سبز در خواهد آمد.
کد مربوط به خط عمودی را درون یک if به شکل زیر قرار دهید:
این باعث می شود که شما فقط وقتی خط عمودی را بکشید که isAddButton برابر با true باشد. به این ترتیب، دکمه ی شما می توانند به شکل + یا - باشد.
در storyboard، دکمه ی خود را انتخاب کنید؛ حالا دو متغیری که به همراه @IBInspectable تعریف کرده بودید در بالای Attributes inspector نمایش داده می شوند:
Is Add Button را به Off تغییر دهید و سپس رنگ دایره را با رفتن به Fill Color ▸ Custom… ▸ Color Sliders ▸ RGB Sliders تعیین کنید. برای رنگ دایره، مقادیر RGB(87, 218, 213) را مانند زیر در ورودی های مربوطه وارد کنید:
این تغییرات بلافاصله در storyboard اعمال می شوند:
خیلی هم عالی! حالا مقدار Is Add Button را به On برگردانید تا دکمه ی شما دوباره تبدیل به دکمه ی + شود.
دکمه ی جدیدی به storyboard اضافه کنید و در سلسله مراتب view ها آن را زیر دکمه ی قبلی قرار دهید. سپس این دکمه را انتخاب کنید و کلاس آن را برابر با PushButton قرار دهید.
در بخش Attributes inspector، مقدار Fill Color را به RGB(238, 77, 77) تغییر دهید و مقدار Is Add Button را برابر با Off قرار دهید. در آخر هم مقدار پیش فرض Default Title را پاک کنید:
حال نوبت آن است برای این دکمه نیز، مانند قبل، محدودیت های Auto Layout را تنظیم کنید. این بخش دقیقا مانند کاری است که پیش از این انجام داده اید:
- در حالی که دکمه در حالت انتخاب شده قرار دارد، از وسط دکمه به سمت چپ عملیات control-drag را انجام دهید به گونه ای که نشانگر ماوس درون دکمه باقی بماند. سپس مقدار widthرا از طریق صفحه ی باز شده تعیین کنید.
- در حالی که دکمه در حالت انتخاب شده قرار دارد، از وسط دکمه به سمت بالا عملیات control-drag را انجام دهید به گونه ای که نشانگر ماوس درون دکمه باقی بماند. سپس مقدار height را از طریق صفحه ی باز شده تعیین کنید.
- از داخل دکمه به خارج سمت چپ دکمه عملیات control-dragرا انجام دهید. سپس گزینه ی Center Horizontally in Container را انتخاب کنید.
- از دکمه ی پایینی به دکمه ی بالایی عملیات control-dragرا انجام دهید و گزینه ی Vertical Spacing را انتخاب کنید.
پس از اینکه محدودیت ها را اعمال کردید، به بخش Size inspector بروید و مقادیر ثابت آنها را مانند زیر اصلاح کنید:
حالا برنامه را اجرا کنید.
در حال حاضر شما یک view قابل تغییر دارید که می توانید آنرا به هر برنامه ای اضافه کنید و این view در هر صفحه نمایشی به شکل واضح نمایش داده خواهد شد.
view بعدی ای که خواهید ساخت در نهایت به شکل زیر در خواهد آمد:
این شکل به مانند این است که انگار درون آن را با رنگ پر کرده ایم اما در حقیقت، این فقط یک مسیر است که عملیات stroke بر روی آن با ضخامت بالایی انجام گرفته است. خطوط پیرامونی نیز از دو مسیر که استفاده از stroke رنگ آمیزی شده اند تشکیل شده.
با رفتن به مسیر File -> New -> File… اقدام به ساخت یک فایل جدید کنید سپس گزینه ی Cocoa Touch Class را انتخاب کنید و نام کلاس جدید را برابر با CounterView قرار دهید. آن را به عنوان زیرکلاسی از UIView تنظیم کنید و اطمینان حاصل کنید که زبان انتخابی برابر با Swift باشد. بر روی دکمه ی Next کلیک کنید و سپس دکمه ی Create را فشار دهید.
کد درون این فایل را با کد زیر جایگزین کنید:
شما اینجا یک struct و چند مقدار ثابت تعریف کرده اید که بعدا از آنها استفاده خواهید کرد. متغیر numberOfGlasses نشان دهنده ی مقدار هدف برای تعداد لیوان های آب مصرف شده در روز است و وقتی که کاربر به این مقدار برسد ، شمارنده ی ما در حالت بیشینه خود قرار خواهد گرفت.
شما در کد بالا سه متغیر به همراه @IBInspectable تعریف کرده اید که می توانید آنها را از طریق storyboard تغییر دهید.
counter تعداد لیوان های آب مصرف شده را در خود ذخیره می کند و از آنجا که دستکاری این متغیر از طریق storyboard می تواند برای آزمایش برنامه سودمند باشد آن را به همراه @IBInspectable تعریف کرده ایم.
به Main.storyboard بروید و یک UIView بالای دکمه ی + اضافه کنید. حالا باید محدودیت های Auto Layout را برای این view تنظیم کنید که روند آن مانند قبل است:
- در حالی که view در حالت انتخاب شده قرار دارد، از وسط آن به سمت چپ عملیات control-dragرا انجام دهید به گونه ای که نشانگر ماوس درون دکمه باقی بماند. سپس مقدار width را از طریق صفحه ی باز شده تعیین کنید.
- در حالی که view در حالت انتخاب شده قرار دارد، از وسط آن به سمت بالا عملیات control-dragرا انجام دهید به گونه ای که نشانگر ماوس درون دکمه باقی بماند. سپس مقدار height را از طریق صفحه ی باز شده تعیین کنید.
- از داخل view به خارج سمت چپ آن عملیات control-drag را انجام دهید. سپس گزینه ی Center Horizontally in Container را انتخاب کنید.
- از view به دکمه ی زیر آن عملیات control-drag را انجام دهید و سپس گزینه ی Vertical Spacingرا انتخاب کنید.
مقادیر ثابتی که برای این محدودیت ها تعیین کردید را از طریق Size inspector اصلاح کنید تا مانند زیر شود:
به بخش Identity inspector بروید و کلاس view اضافه شده را به CounterView تغییر دهید. حال شما هر طراحی ای که در تابع draw(_ :) انجام دهید درون این view ظاهر خواهد شد. اما شما هنوز چیزی به این تابع اضافه نکرده اید.
ما این آموزش را برای یادگیری یک مبحث مختصر ریاضی به وقفه می اندازیم.
طراحی کردن کمان ها درون context، بر اساس دایره ی زیر صورت می پذیرد. این دایره، دایره ای به شعاع یک واحد است:
فلش قرمز نشان می دهد که کمان شما، در جهت ساعتگرد، از کجا شروع شده و در کجا پایان می یابد. شما یک کمان رسم خواهید کرد که از مکان 3π/4 رادیان یعنی 135 درجه شروع می شود و به صورت ساعتگرد در مکان π/4 رادیان یعنی 45 درجه پایان می یابد.
رادیان در برنامه نویسی عموما به جای درجه به کار می رود و فکر کردن در واحد رادیان به ما کمک می کند که برای کار کردن با کمان ها دیگر نیاز به تبدیل درجه به رادیان نداشته باشیم. البته باید به این نکته نیز توجه کنید که شما بعدا نیاز به محاسبه ی طول کمان ها خواهید داشت که رادیان در آنجا نیز به کمک شما خواهد آمد.
مقدار عددی طول کمانی از دایره به شعاع واحد، برابر با مقدار عددی زاویه ی مربوط به آن است. به عنوان مثال در تصویر بالا طول کمانی از 0 درجه تا 90 درجه برابر با π/2 خواهد بود. برای محاسبه ی طول کمان در موقعیت های فیزیکی و واقعی باید مقدار عددی زاویه ی مربوط به کمان را در شعاع دایره ضرب کنیم.
برای محاسبه ی طول فلش قرمز در تصویر بالا، باید اندازه ی زاویه ی دربرگیرنده ی آن را به رادیان مشخص کنیم:
2π –3π/4 + π/4 = 3π/2
که در واحد درجه برابر با 270 درجه خواهد بود.
در اینجا درس مصلحتی ما پایان می یابد!
در CounterView.swift کد زیر را به تابع draw(_ :) اضافه کنید تا کمان مورد نظر شما ساخته شود:
کاری که این قطعه کد انجام می دهد به شرح زیر است:
1. تعریف نقطه ی مرکز که کمان شما حول آن تشکیل می شود
2. محاسبه ی شعاع بر اساس بزرگترین بُعد view
3. تعریف زوایای شروع و پایان برای کمان
4. ساختن مسیری بر اساس نقطه ی مرکز، شعاع و زوایایی که تعریف کرده اید
5. تعیین ضخامت و رنگ مسیر پیش از اعمال عملیات stroke
فرض کنید که این طراحی را با استفاده از یک پرگار انجام می دهید. شما سوزن پرگار را بر روی نقطه ی مرکزی قرار می دهید و سپس بازوی پرگار را به اندازه شعاع مورد نظر باز می کنید. در نهایت هم این پرگار را به اندازه زاویه ی مورد نظر خود می چرخانید تا کمان شما کشیده شود.
در قطعه کد بالا، center معادل با نقطه ی سوزن پرگار است و radius میزان گشودگی بازوی پرگار را منهای نصف ضخامت کمان نهایی در خود ذخیره می کند. ضخامت کمانی نهایی نیز برابر با ضخامت خودکار سر پرگار است.
نکته: مباحث بالا، به طوری کلی تمام چیزی است که نیاز است درباره ی کشیدن کمان ها بدانید ولی اگر می خواهید در این مبحث عمیق تر شوید می توانید از لینک زیر کمک بگیرید: http://www.raywenderlich.com/33193/core-graphics-tutorial-arcs-and-paths
برنامه را اجرا کنید و حالا باید چیزی شبیه تصویر زیر مشاهده کنید:
وقتی که شما یک لیوان خنک آب را نوش جان کرده اید، خطوطی در پیرامون کمان قبلی به شما کمک می کنند تا بتوانید روند خود را برای رسیدن به هدف 8 لیوان در روز مشاهده کنید. این خطوط پیرامونی از دو کمان و دو خط تشکیل شده است که یک کمان بیرونی، یک کمان درونی و خطوط وصل کننده ی این دو را شامل می شود.
در CounterView.swift قطعه کد زیر را به تابع draw(_ :) اضافه کنید:
چند نکته در مورد کد بالا وجود دارد:
1. outlineEndAngleمشخص کننده ی زاویه ی پایان کمان های پیرامونی است که با استفاده از مقدار ذخیره شده در متغیر counterمحاسبه می شود.
2. outlinePathمسیر مشخص کننده ی کمان بیرونی است. UIBezierPath() مقدار مربوط به شعاع را ورودی می گیرد تا طول کمان را محاسبه کند چرا که این کمان بخشی از دایره ای با شعاع واحد نمی باشد.
3. این کد سپس یک کمان درونی به کمان قبلی اضافه می کند. این کمان، زاویه ی یکسانی با کمان بیرونی دارد اما در جهت معکوس کشیده می شود و دلیل اینکه ورودی clockwise را برابر با false قرار داده ایم نیز همین است. همچنین این کار به صورت خودکار یک خط بین کمان بیرونی و درونی رسم می کند که این دو کمان را از طریق وصل کردن نقطه ی پایان کمان بیرونی و نقطه ی شروع کمان درونی به یکدیگر متصل می گرداند.
4. بستن مسیر به صورت خودکار باعث می شود تا خطی دیگر بین این دو کمان رسم شود که نقطه ی پایان کمان درونی را به نقطه ی شروع کمان بیرونی متصل می کند.
با مساوی قرار دادن مقدار متغیر counter با 5 می توانیم ببینیم که CounterView به چه شکلی در خواهد آمد که باید مانند زیر باشد:
Main.storyboard را باز کنید و CounterView را انتخاب کنید. در بخش Attributes inspector مقدار Counter را برای آزمودن کد خود تغییر دهید. مشاهده می کنید که این کار به صورت کاملا تعاملی انجام می پذیرد. مقادیر بزرگتر از 8 و کوچکتر از 0 را نیز آزمایش کنید.
در آخر هم مقدار Counter Color را برابر با RGB(87, 218, 213) قرار دهید و مقدار Outline Color را به RGB(34, 110, 100) تغییر دهید:
شما تا این جای کار، همه ی کنترل کننده ها و کنترل شونده های مورد نیاز را ساخته اید؛ حالا نوبت این است که منطق مربوط به کم و زیاد شدن متغیر counter توسط دکمه های + و - را پیاده سازی کنیم.
در Main.storyboard یک UILabel را به وسط Counter View بکشید و مطمئن شوید که در سلسله مراتب view ها در زیر Counter View قرار دارد. محدودیت هایی نیز برای اینکه این view از لحاظ عمودی و افقی در وسط قرار بگیرد به آن اضافه کنید. وقتی که کارتان تمام شد، محدودیت ها باید مانند زیر شده باشند:
به بخش Attributes inspector بروید و Alignment را برابر با center قرار دهید. همچنین میزان font size را به 36 و مقدار پیشفرض Label را به 8 تغییر دهید.
به ViewController.swift بروید و این متغیر ها را به بالای کلاس اضافه کنید:
همچنین تابع زیر را به آخر کلاس اضافه کنید:
شما در کد بالا، منطق مربوط به اضافه یا کم شدن counter را بر اساس نوع دکمه ای که فشار داده شده پیاده سازی کرده اید. دقت کنید که اگرچه می توانستیم اجازه دهیم مقدار counter کمتر از 0 بشود اما برای برنامه ای که ما در حال توسعه ی آن هستیم بی معنی است؛ هیچ کس نمی تواند مقدار منفی آب بنوشد!
در این تابع همچنین مقداری که در Label نمایش داده می شود نیز بروز رسانی می شود.
حالا باید خط زیر را به آخر viewDidLoad() اضافه کنیم تا از بروز شدن مقدار اولیه ی Label مطمئن شویم:
به Main.storyboard بروید و outlet های CounterView و UILabel را به متغیر های مربوطه متصل کنید و تابع pushButtonPressed را به رویداد های Touch Up Inside هر دو دکمه متصل کنید:
برنامه را اجرا کنید. چک کنید که آیا فشار دادن دکمه ها باعث تغییر عدد Label می شود یا خیر که اگر کد ها را به درستی وارد کرده باشید، باید تغییر کند. ممکن است متوجه این موضوع بشوید که فشار دادن این دکمه ها باعث بروز رسانی Counter View نمی شود؛ برای فهم این مشکل باید آنچه را که در ابتدای آموزش گفتیم به یاد بیاوریم: تابع draw(_ :) فقط وقتی صدا زده می شود که یا view های روی آن حرکت کنند، یا مقدار متغیر hidden تغییر کند، یا view به تازگی وارد صفحه شده باشد و یا اینکه توابع setNeedsDisplay() یا setNeedsDisplayInRect() صدا زده شده باشند. ما می خواهیم که هر وقت متغیر counter تغییر کرد، Counter View نیز بروز رسانی شود وگرنه برنامه ی ما به شکل مطلوبی کار نمی کند.
به Counter View بروید و تعریف متغیر counter را به شکل زیر تغییر دهید:
این کد هر بار که مقدار متغیر counter تغییر می کند، اگر مقدار آن کمتر یا مساوی مقدار هدف برای تعداد لیوان های آب در هر روز باشد، یک دور Counter View را بروز رسانی می کند.
برنامه را اجرا کنید، حالا دیگر همه چیز باید به خوبی کار کند:
می توانید پروژه ی کامل را از طریق گزینه ی Download Materials در بالا و یا پایین صفحه در آدرس https://www.raywenderlich.com/8003281-core-graphics-tutorial-getting-started دریافت کنید.
شما پس از اتمام این آموزش، مفاهیم پایه ی طراحی کردن به کمک Core Graphics را فرا گرفته اید. حالا می توانید شکل و اندازه ی view ها را در پروژه ی خود به دلخواه تغییر دهید.
در بخش دوم این آموزش به آدرس https://www.raywenderlich.com/10946920-core-graphics-tutorial-gradients-and-contexts با مفاهیم context ها در Core Graphics بیشتر آشنا خواهید شد و خواهید توانست تا نموداری برای نشان دادن میزان مصرف آب روزانه طراحی کنید.
اگر به یادگیری بیشتر درباره ی layout های شخصی سازی شده علاقه دارید می توانید از منابع زیر استفاده کنید:
- منابعی که توسط Apple در این زمینه در اختیار توسعه دهندگان قرار گرفته اند را می توانید از این آدرس مشاهده کنید: https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/Introduction/Introduction.html#//apple_ref/doc/uid/TP30001066
- اگر آموزش تصویری و ویدئویی را ترجیح می دهید می توانید به آدرس زیر بروید و آموزش تصویری ما را در این زمینه مشاهده کنید: https://www.raywenderlich.com/3402-beginning-core-graphics