В процессе изучения Objective-C и iOS-разработки не могла понять принципы работы блоков. С толку сбивало, что их можно передавать как параметры в методы. Наткнулась на статью, которая показалась мне крайне интересной, так как рассматривались не только блоки, но и процесс разработки приложения. Пост адаптирован под xCode 7.3.1.
Предисловие
Блоки — невероятно мощное добавление к C/Objective-C, позволяющее «обернуть» куски кода в отдельные единицы и оперировать ими в качестве объектов. Все больше и больше API требуют использование блоков в iOS, поэтому очень важно их понимать. Однако, их синтаксис и некоторые тонкие аспекты зачастую сбивают с толку начинающих. Но бояться не стоит — этот урок будет весьма полезным.
В двух частях этого урока мы создадим небольшой iOS проект «iOS Diner». Приложение само по себе простое: пользователь выбирает блюдо из меню для создания заказа, как будто бы собираясь пообедать. Осторожно: в процессе создания обостряется чувство голода!
В первой части разработаем UI нашего приложения, заодно рассмотрев Storyboard (дословно — раскадровка — Прим. пер.), включая небольшую памятку по созданию и использованию веб-сервисов для загрузки меню в формате JSON.
Примечание. Если вы уже довольно хорошо разбираетесь со Storyboard и Interface Builder, можете пропустить первую часть и сразу перейти ко второй части, где мы начнем использовать блоки. Эта часть фокусируется только на Storyboard и Interface Builder.
Во второй части будет показано всестороннее использование блоков для программирования логики приложения, асинхронной обработки данных, фоновых задач, использования в качестве альтернативы многим стандартным API, и многое другое.
Начинаем
Открываем Xcode и создаем новый проект.
В названии проекта укажите «iOSDiner».
Запустите проект. Конечно, он еще пустой, и на симуляторе будет пустой белый экран.
В настройках проекта в Deployment Info убираем галочку Portrait.
Первое, что мы сделаем, — настроим представление. Для этого понадобится немного графики. Скачать можно отсюда. Это нужно будет добавить в ресурсы проекта.
Если честно, мне никогда не нравилось, как xCode обрабатывает совпадение файлов в проекте и файловой системе, поэтому обычно я добавляю ресурсы вручную в файловой системе. В Finder откройте папку проекта и в ней создайте папку «Resources». В ней создайте папку «Images».
Скопируйте графику из загруженного ZIP-файла в папку Images, затем перетащите папку Resources в папку IOSDiner в Xcode, как показано на скриншоте ниже.
Теперь можно увидеть папку Resources в Xcode, в которой есть подпапка Images с загруженными изображениями, — как и в файловой системе.
Добавление изображений
Откройте Main.storyboard.
Если вы не видите вторую колонку с названием «View Controller Scene», внизу кликните на кнопку Expand.
Итак, мы собираемся добавить изображения в Storyboard в виде UIImageViews и UIButtons. Чтобы было проще, откройте боковую панель Utilities и выберите Media Library.
Здесь мы видим все раннее добавленные в проект изображения. Наверняка вы заметили, что у каждой картинки есть своя копия с «@2x» в конце названия. Она используется для retina-версии.
Мы заинтересованы только в обычной картинке. Вы можете проверить, какая это версия картинки, кликнув и нажав клавишу пробел. Перетащите “bg_wall.png” на root-view, как показано ниже. Если вы не уверены, что изображение поставлено корректно, можете переключиться в Size Inspector для изменения его X и Y координат.
Теперь проделайте то же самое со следующими изображениями:
- person.png
- sign_theiOSdiner.png
- chalkboard.png
- bg_counter.png
- total_field.png
- food_box.png
Примечание. Вы можете заставить Image View соответствовать точным размерам картинки, нажав одновременно Cmd и клавишу =. Расставив все картинки на свои места, запустите проект.
Вау! Почти похоже на приложение! Далее, добавим части пользовательского интерфейса. На панели Utilities переключитесь на Object Library.
Перетащите Button в центр нашего представления, над монитором. Дважды кликните на только что добавленную кнопку и напишите «-1».
Убедитесь, что кнопка выделена. В Attributes Inspector установите атрибут Background «button_silver.png».Нажмите и удерживайте клавишу Alt и перетащите кнопку -1 вправо. Так создастся копия объекта. Измените ее текст на «+1».
Перетащите еще одну кнопку на левый край монитора. Установите Button Type в Custom, удалите заголовок кнопки и из Media Library перетащите на кнопку «button_arrow_left.png»
Скопируйте эту кнопку и поменяйте фоновое изображение на «button_arrow.png»
И осталась последняя кнопка. Разместите ее под доской и установите фоновое изображение «total_field.png». Запустите проект.
Выглядит вполне симпатично. Следующее, что мы добавим, — лейблы и окошко предварительного просмотра.
Снова идем в Object Library. Перетаскиваем UILabel на доску и растягиваем его по размеру доски.
В Atrributes Inspector установите Lines в 0 (это сделает лейбл многострочным), поменяйте Text Color на белый, и Font — на Marker Felt 17.0. Обычно использование Marker Felt я считаю преступлением, но в нашем случае он вполне подходит.
Перетащите UIImageView на монитор и растяните по его размеру.
В Attributes Inspector поменяйте Mode на Aspect Fit.
Перетащите еще один UILabel на табличку в правом нижнем углу. Сделайте его по размеру серой зоны таблички и установите Alignment в Center.
Устанавливаем IBOutlets и IBActions
Далее необходимо установить связи между пользовательским интерфейсом, который мы только что создали, и кодом. Для этого и нужны IBOutlets и IBActions. IB означает Interface Builder, который используется для создания UI в xCode.
- IBOutlet, в основном, — это связь элемента UI (кнопки или лейбла) с его ссылкой в коде.
- IBAction — это действие (или метод, как удобнее) в коде, которое можно подключить к некоторому событию (нажатие кнопки, например) в разработанном интерфейсе.
Итак, давайте напишем немного кода для подключения элементов UI через IBOutlets и IBActions.
Закройте вкладку Utilities и откройте Assistant editor. В зависимости от того, как он настроен, экран может выглядеть не так, как на скриншоте. Если вы хотите изменить отображение Assistant editor, загляните в его меню.
Начнем с кнопок. Выберите кнопку «-1», удерживайте клавишу Ctrl и перетащите ее в код. Это автоматизирует создание IBOutlet для кнопки.
Для этого объекта все, что нужно, — назвать его. Мне нравятся префиксы «ib», так как в xCode становится легко найти все элементы по автозаполнению. Назовите этот объект «ibRemoveItemButton» и кликните Connect.
Аналогично делаем с остальными кнопками.
Точно так же устанавливаем лейблы.
Теперь ViewController.m должен выглядеть так:
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIButton *ibRemoveItemButton;
@property (weak, nonatomic) IBOutlet UIButton *ibAddItemButton;
@property (weak, nonatomic) IBOutlet UIButton *ibPreviousItemButton;
@property (weak, nonatomic) IBOutlet UIButton *ibNextItemButton;
@property (weak, nonatomic) IBOutlet UIButton *ibTotalOrderButton;
@property (weak, nonatomic) IBOutlet UILabel *ibChalkboardLabel;
@property (weak, nonatomic) IBOutlet UIImageView *ibCurrentItemImageView;
@property (weak, nonatomic) IBOutlet UILabel *ibCurrentItemLabel;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
Теперь мы должны добавить IBActions для UIButtons. Это методы, которые вызываются в ответ на определенные события (Touch Up Inside, Touch Up Outside, Touch Cancel, и т.д.). Для кнопок чаще всего используется Touch Up Inside.
Выберем кнопку «-1». Снова с помощью клавиши Ctrl перетаскиваем ее в ViewController.m. Так как это IBAction, используем префикс «iba». Повторяем для всех кнопок.
Устанавливаем веб-сервис
Прежде чем приступить к коду, настроим веб-сервис. Я не собираюсь объяснять все в мельчайших подробностях, так как для этого уже существует несколько уроков (How To Write A Simple PHP/MySQL Web Service for an iOS App и How to Write an iOS App That Uses a Web Service).
Код ниже показывает, как будет выглядеть PHP код для веб-сервиса.
<?php
function getStatusCodeMessage($status) {
$codes = Array(
100 => 'Continue',
101 => 'Switching Protocols',
200 => 'OK',
201 => 'Created',
202 => 'Accepted',
203 => 'Non-Authoritative Information',
204 => 'No Content',
205 => 'Reset Content',
206 => 'Partial Content',
300 => 'Multiple Choices',
301 => 'Moved Permanently',
302 => 'Found',
303 => 'See Other',
304 => 'Not Modified',
305 => 'Use Proxy',
306 => '(Unused)',
307 => 'Temporary Redirect',
400 => 'Bad Request',
401 => 'Unauthorized',
402 => 'Payment Required',
403 => 'Forbidden',
404 => 'Not Found',
405 => 'Method Not Allowed',
406 => 'Not Acceptable',
407 => 'Proxy Authentication Required',
408 => 'Request Timeout',
409 => 'Conflict',
410 => 'Gone',
411 => 'Length Required',
412 => 'Precondition Failed',
413 => 'Request Entity Too Large',
414 => 'Request-URI Too Long',
415 => 'Unsupported Media Type',
416 => 'Requested Range Not Satisfiable',
417 => 'Expectation Failed',
500 => 'Internal Server Error',
501 => 'Not Implemented',
502 => 'Bad Gateway',
503 => 'Service Unavailable',
504 => 'Gateway Timeout',
505 => 'HTTP Version Not Supported'
);
return (isset($codes[$status])) ? $codes[$status] : '';
}
// Helper method to send a HTTP response code/message
function sendResponse($status = 200, $body = '', $content_type = 'text/html') {
$status_header = 'HTTP/1.1 ' . $status . ' ' . getStatusCodeMessage($status);
header($status_header);
header('Content-type: ' . $content_type);
echo $body;
}
class InventoryAPI {
function getInventory() {
$inventory = array(
array("Name"=>"Hamburger","Price"=>0.99,"Image"=>"food_hamburger.png"),
array("Name"=>"Cheeseburger","Price"=>1.20,"Image"=>"food_cheeseburger.png"),
array("Name"=>"Fries","Price"=>0.69,"Image"=>"food_fries.png"),
array("Name"=>"Onion Rings","Price"=>0.69,"Image"=>"food_onion-rings.png"),
array("Name"=>"Soda","Price"=>0.75,"Image"=>"food_soda.png"),
array("Name"=>"Shake","Price"=>1.20,"Image"=>"food_milkshake.png")
);
sendResponse(200, json_encode($inventory));
}
}
sleep(5);
$api = new InventoryAPI;
$api->getInventory();
?>
Этот веб-сервис представляет собой вполне простой PHP-скрипт, который возвращает JSON-массив. В массиве находится так называемый ассоциативный массив (PHP), или словарь(Objective-C), в котором содержится название, цена и имя изображения для предмета. Единственное, что следует отметить в вышеуказанном коде, — это строчка sleep(5);
.
Я использую ее для симуляции медленного веб-сервиса, чтобы лучше показать, как блоки могут помочь с асинхронными операциями.
Вы можете скопировать этот код в файл с расширением .php и разместить его на
Что делать дальше
Проект этой части урока можно скачать здесь. Что ж, здесь было много всяких вещей, никак не связанных с блоками. Но сейчас, установив view и веб-сервис, мы можем перейти к кодингу, где и будем использовать блоки, чтобы сделать наше приложение полностью функционирующим.
Автор: SlipRam