10 September 2010

Кроссплатформенный код для приложений под iPhone и iPad

Pravo.ru corporate blog
Учимся парадигме Model-View-Presenter и выкидываем в AppStore кучу вкусностей для iPhone и iPad сразу

Как наиболее оптимальным образом портировать приложение, написанное под iPhone для iPad, можите решить только вы сами. Могу лишь предложить несколько рецептов, которые будут удобны в использовании на данном конкретном примере Web-приложений.

Далее будет предложена парадигма организации кода, рассмотрены компоненты, доступные в SDK 3.2. А паттерны проектирования вы изучите сами :-)

Начинается все с дизайна. Как правило, несколько экранов iPhone пытаются уложить на одном экране iPad.
image

При написании пользовательских интерфейсов для программ под платформу iOS используется парадигма Model-View-Controller. При этом она достаточно неудобна при портировании и поддержки приложений под iPhone и iPad. О ней вы можете узнать в Википедии.

image

На изображении видно, что присутствует связь между моделью и представлением, которая нарушает свободу в повторном использовании модели. Также она делает несколько тяжелым контролер, который тоже предстоит переписывать под iPad.

Взамен я предлагаю использовать, активно продвигаемую на платформе .Net парадигму, представленную впервые компанией Microsoft. Она произошла из предыдущей путем замены Controller на Presenter, который все взаимодействие модели и представления берет на себя, разрывая связь представления с моделью. В итоге модель становится более универсальной и уменьшается код Controller.
image

Как сократить объем написанного кода и, соответственно, уменьшить число ошибок под iOS
Следует начать проект со стратегии организации кода. В приведенных ниже примерах используются настройки проекта и создание представления кнопки btContinue в коде. Эти представления для iPhone и iPad различаются. Когда мы компилируем два приложения под оба устройства изменения должны контролироваться макросами, которые изменяют поведение на этапе компиляции. Изменения компилируются и жестко прошиваются в приложение. Используем #define IPAD. Получаем два приложения: одно для iPhone, другое для iPad
image

Изменения контролируются во время выполнения приложения. Используем UIDevice. Получаем одно приложение: которое запускается и под iPhone и под iPad
image

В случае создания универсального приложения определение представления происходит в режиме исполнения кода, соответственно эта операция будет тормозить работу программы.

Как видно из данного примера оба подхода практически ничем не отличаются по количеству написанного кода, но первый вариант должен работать быстрее.

Из личного опыта
Люди очень активно пользуются юридическим софтом. Например, проект «Картотека» (картотека арбитражных дел) в AppStore набирает более 100 скачиваний в день
image

При написании картотеки арбитражных дел нельзя было использовать стандартные компоненты, доступные в SDK 3.2, такие как UISplitViewController и UIPopoverController. Например, первый не поддерживает работу UINavigationController и должен быть главным экраном. У второго компонента отсутствует обратная совместимость с iPhone. Выход: писать свои компоненты. Кстати сторонние разработчики уже делали свои варианты. Об этом есть, например, статья на Хабре

При желание можно больше и еще больше и гораздо больше поместить на экране iPad информации, заимствованной с экранов iPhone. В проекте картотека присутствовала обратная совместимость, то есть для удобства использования перехода от дела к инстанциям дела использовался выпадающий список UIPopoverController, который по непонятным причинам имеет запрет на использование на iPhone, причем выясняется это на этапе исполнения кода. Выход: ПИШИТЕ СВОИ КОМПОНЕНТЫ.

Немного практики
Обычно на практике представление формируется прямо в Presenter.
В парадигме Apple экраны (представления) должны храниться в ресурсах, но это очень неудобно. Нужно постоянно поддерживать связи с Controller, не говоря уже о модели. Было принято решение, что формировать представление прямо в Presenter будет наиболее эффективно. При этом код самого презентора увеличивать совсем не обязательно, даже напротив — его можно выделять в классы и даже подклассы, как показано в данном примере для элемента списка.
image

Необходимо помнить о том что мы имеем дело со слабым аппаратным обеспечением, для которого процессорное время и тем более память становятся очень критичными ресурсами. Соответственно, бывают ситуации в которых хранение представления в ресурсах оправданно. Тем более, что именно для данного примера кода это совершенно оправданно, т.к. лишних связей и обслуживающего эти связи кода просто нет. Все-таки в xib (nib) можно хранить UIView, например такой как UITableViewCell:
image

Но это совершенно не означает, что для разных реализаций iPhone/iPad этот ресурс xib (nib) не может быть единым!
Люди часто боятся нового. Они не хотят реанимировать макросы и давать им новую жизнь даже там, где они становятся актуальными. Дело в том, что мы их просто не умеем готовить. Это не только полезная снедь, как овсянка, но и очень вкусная, если ее приправить бабушкиным варением, например. Обратите внимание на два примера объявления полей одного интерфейса (класса). В последнем, кстати говоря, исправлена ошибка первого. Но какой эффект: не нужно переправлять там где что то забыл подобное и компактность, а самое главное понятность кода!

Неизбежное использование макросов (отвратительно!)
image

Желательное использование макросов (так-то лучше!)
image

Итоги
Во-первых, нужно определить настройки проекта(ов) для определения представления (View) на этапе компиляции или выполнения. Затем необходимо избавиться от связей Model-View путем переноса логики их взаимодействия в Presenter.
Максимально эффективно отвязываемся от представлений, формируемых посредством xib, оставляя в них только атомарные полотна (например, такие как UIViewTableCell).
Потом определяем макросы, ограничивающие представление под устройство iPhone/iPad. И не забудьте, что Objective-C является «Message Oriented Program» языком (пишем свою VCL).
Tags:Model-View-PresenteriOSUIViewTableCelliPhoneiPadUIPopoverControllerPresenter
Hubs: Pravo.ru corporate blog
+41
13.7k 65
Comments 105