Использование блоков в iOS. Часть 1

в 13:19, , рубрики: ios development, objective-c, разработка под iOS

В процессе изучения 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 и создаем новый проект.

image

В названии проекта укажите «iOSDiner».

Использование блоков в iOS. Часть 1 - 2

Запустите проект. Конечно, он еще пустой, и на симуляторе будет пустой белый экран.

Использование блоков в iOS. Часть 1 - 3

В настройках проекта в Deployment Info убираем галочку Portrait.

Использование блоков в iOS. Часть 1 - 4

Первое, что мы сделаем, — настроим представление. Для этого понадобится немного графики. Скачать можно отсюда. Это нужно будет добавить в ресурсы проекта.

Если честно, мне никогда не нравилось, как xCode обрабатывает совпадение файлов в проекте и файловой системе, поэтому обычно я добавляю ресурсы вручную в файловой системе. В Finder откройте папку проекта и в ней создайте папку «Resources». В ней создайте папку «Images».

Использование блоков в iOS. Часть 1 - 5

Скопируйте графику из загруженного ZIP-файла в папку Images, затем перетащите папку Resources в папку IOSDiner в Xcode, как показано на скриншоте ниже.

Использование блоков в iOS. Часть 1 - 6

Теперь можно увидеть папку Resources в Xcode, в которой есть подпапка Images с загруженными изображениями, — как и в файловой системе.

Добавление изображений

Откройте Main.storyboard.

image

Если вы не видите вторую колонку с названием «View Controller Scene», внизу кликните на кнопку Expand.

Итак, мы собираемся добавить изображения в Storyboard в виде UIImageViews и UIButtons. Чтобы было проще, откройте боковую панель Utilities и выберите Media Library.

Использование блоков в iOS. Часть 1 - 8

Здесь мы видим все раннее добавленные в проект изображения. Наверняка вы заметили, что у каждой картинки есть своя копия с «@2x» в конце названия. Она используется для retina-версии.

Мы заинтересованы только в обычной картинке. Вы можете проверить, какая это версия картинки, кликнув и нажав клавишу пробел. Перетащите “bg_wall.png” на root-view, как показано ниже. Если вы не уверены, что изображение поставлено корректно, можете переключиться в Size Inspector для изменения его X и Y координат.

Использование блоков в iOS. Часть 1 - 9

Использование блоков в iOS. Часть 1 - 10

Теперь проделайте то же самое со следующими изображениями:

  • person.png
  • sign_theiOSdiner.png
  • chalkboard.png
  • bg_counter.png
  • total_field.png
  • food_box.png

Примечание. Вы можете заставить Image View соответствовать точным размерам картинки, нажав одновременно Cmd и клавишу =. Расставив все картинки на свои места, запустите проект.

Использование блоков в iOS. Часть 1 - 11

Вау! Почти похоже на приложение! Далее, добавим части пользовательского интерфейса. На панели Utilities переключитесь на Object Library.

Перетащите Button в центр нашего представления, над монитором. Дважды кликните на только что добавленную кнопку и напишите «-1».

Использование блоков в iOS. Часть 1 - 12

Round Rect Button

В оригинале использовались Round Rect Button, которые отсутствуют в xCode 7.3.1. На Stack Overflow есть решение, как их использовать.

Убедитесь, что кнопка выделена. В Attributes Inspector установите атрибут Background «button_silver.png».Нажмите и удерживайте клавишу Alt и перетащите кнопку -1 вправо. Так создастся копия объекта. Измените ее текст на «+1».

Использование блоков в iOS. Часть 1 - 13

Использование блоков в iOS. Часть 1 - 14

Перетащите еще одну кнопку на левый край монитора. Установите Button Type в Custom, удалите заголовок кнопки и из Media Library перетащите на кнопку «button_arrow_left.png»

Использование блоков в iOS. Часть 1 - 15

Скопируйте эту кнопку и поменяйте фоновое изображение на «button_arrow.png»

Использование блоков в iOS. Часть 1 - 16

И осталась последняя кнопка. Разместите ее под доской и установите фоновое изображение «total_field.png». Запустите проект.

Использование блоков в iOS. Часть 1 - 17

Выглядит вполне симпатично. Следующее, что мы добавим, — лейблы и окошко предварительного просмотра.
Снова идем в Object Library. Перетаскиваем UILabel на доску и растягиваем его по размеру доски.

Использование блоков в iOS. Часть 1 - 18

В Atrributes Inspector установите Lines в 0 (это сделает лейбл многострочным), поменяйте Text Color на белый, и Font — на Marker Felt 17.0. Обычно использование Marker Felt я считаю преступлением, но в нашем случае он вполне подходит.

Использование блоков в iOS. Часть 1 - 19

Перетащите UIImageView на монитор и растяните по его размеру.

Использование блоков в iOS. Часть 1 - 20

В Attributes Inspector поменяйте Mode на Aspect Fit.

Использование блоков в iOS. Часть 1 - 21

Перетащите еще один UILabel на табличку в правом нижнем углу. Сделайте его по размеру серой зоны таблички и установите Alignment в Center.

Использование блоков в iOS. Часть 1 - 22

Устанавливаем IBOutlets и IBActions

Далее необходимо установить связи между пользовательским интерфейсом, который мы только что создали, и кодом. Для этого и нужны IBOutlets и IBActions. IB означает Interface Builder, который используется для создания UI в xCode.

  • IBOutlet, в основном, — это связь элемента UI (кнопки или лейбла) с его ссылкой в коде.
  • IBAction — это действие (или метод, как удобнее) в коде, которое можно подключить к некоторому событию (нажатие кнопки, например) в разработанном интерфейсе.

Итак, давайте напишем немного кода для подключения элементов UI через IBOutlets и IBActions.

Закройте вкладку Utilities и откройте Assistant editor. В зависимости от того, как он настроен, экран может выглядеть не так, как на скриншоте. Если вы хотите изменить отображение Assistant editor, загляните в его меню.

Использование блоков в iOS. Часть 1 - 23

Начнем с кнопок. Выберите кнопку «-1», удерживайте клавишу Ctrl и перетащите ее в код. Это автоматизирует создание IBOutlet для кнопки.

Для этого объекта все, что нужно, — назвать его. Мне нравятся префиксы «ib», так как в xCode становится легко найти все элементы по автозаполнению. Назовите этот объект «ibRemoveItemButton» и кликните Connect.

Использование блоков в iOS. Часть 1 - 24

Аналогично делаем с остальными кнопками.

Использование блоков в iOS. Часть 1 - 25

Использование блоков в iOS. Часть 1 - 26

Использование блоков в iOS. Часть 1 - 27

Использование блоков в iOS. Часть 1 - 28

Точно так же устанавливаем лейблы.

Использование блоков в iOS. Часть 1 - 29

Использование блоков в iOS. Часть 1 - 30

Использование блоков в iOS. Часть 1 - 31

Теперь 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». Повторяем для всех кнопок.

Использование блоков в iOS. Часть 1 - 32

Использование блоков в iOS. Часть 1 - 33

Использование блоков в iOS. Часть 1 - 34

Использование блоков в iOS. Часть 1 - 35

Использование блоков в iOS. Часть 1 - 36

Устанавливаем веб-сервис

Прежде чем приступить к коду, настроим веб-сервис. Я не собираюсь объяснять все в мельчайших подробностях, так как для этого уже существует несколько уроков (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

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js