Статьи

Представляем MarkupKit

MarkupKit — это фреймворк с открытым исходным кодом для упрощения разработки приложений для iOS. Это позволяет разработчикам создавать пользовательские интерфейсы декларативно, используя понятный человеку язык разметки, а не программно в коде или интерактивно, используя инструмент визуального моделирования, такой как Interface Builder.

Создание интерфейса в разметке позволяет легко визуализировать полученный результат, а также распознавать различия между ревизиями. Это также метафора, которая удобна для многих разработчиков благодаря повсеместному распространению HTML и World Wide Web.

Эта статья знакомит с платформой MarkupKit и предоставляет обзор некоторых ее основных функций.

СТРУКТУРА ДОКУМЕНТА

MarkupKit использует XML для определения структуры пользовательского интерфейса. В документе MarkupKit элементы XML представляют экземпляры  UIView подклассов, а атрибуты XML обычно представляют свойства, связанные с этими представлениями. Например, следующая разметка объявляет экземпляр  UILabel и устанавливает значение его  text свойства «Hello, World!»:

<UILabel text="Hello, World!"/>

Вывод, создаваемый этой разметкой, идентичен выводу следующего кода:

let label = UILabel()
label.text = "Hello, World!"

MarkupKit использует кодирование значения ключа (KVC) для установки значений свойства. Имя атрибута представляет путь устанавливаемого свойства (например, атрибут «текст», показанный в предыдущем примере). Однако, поскольку имя атрибута представляет собой путь к ключу, атрибуты также можно использовать для установки значений вложенных свойств. Например, следующая разметка создает экземпляр  свойства UITableViewCell текстовой метки  textкоторого установлено в «Это строка таблицы»:

<UITableViewCell textLabel.text="This is a table row"/>

Преобразования типов для строковых, числовых и логических свойств обрабатываются KVC автоматически. Другие типы, такие как перечисления, цвета, шрифты и изображения, обрабатываются специально MarkupKit. Например, следующая разметка устанавливает шрифт текста в предыдущем примере как Helvetica Bold с 24 точками, а цвет текста — красным. MarkupKit переводит имя шрифта и шестигранный-кодированное значение цвета  UIFont и  UIColor случаи, соответственно, перед установкой значения свойств:

<UITableViewCell textLabel.text="This is a table row"
    textLabel.font="Helvetica-Bold 24" 
    textLabel.textColor="#ff0000"/>

LOCALIZATION

Если значение атрибута начинается с «@», MarkupKit пытается найти локализованную версию значения перед установкой свойства. Например, если приложение определило локализованное приветствие в  Localizable.strings  следующим образом:

"hello" = "Hello, World!";

следующая разметка создаст экземпляр  UILabel со значением его textсвойства, равным  «Hello, World!»:

<UILabel text="@hello"/>

В дополнение к глобальным значениям, определенным в  Localizable.stringsstrings инструкция обработки (PI) может использоваться для определения набора локальных строковых значений, которые видны только текущему документу. Это может помочь упростить управление локализованными ресурсами, поскольку они могут быть разбиты на несколько небольших контекстно-зависимых частей, а не на один большой набор строковых значений в масштабах всего приложения.

ЗАБРОНИРОВАННЫЕ АТРИБУТЫ

Некоторые атрибуты имеют особое значение в MarkupKit и не представляют свойства представления. К ним относятся  стилькласс и  идентификатор . Атрибут «style» используется для вызова «фабричного метода» для создания экземпляра представления. Атрибут «class» указывает набор свойств шаблона, которые применяются к экземпляру, похожему на стили в CSS. Атрибут «id» используется, чтобы определить «выход» для представления, подобного выходам в XCode.

Кроме того, атрибуты, имя которых начинается с «on», зарезервированы. За исключением существующих свойств, имена которых начинаются с «on», эти атрибуты представляют обработчики событий или «действия», аналогичные действиям в Xcode.

Каждый из зарезервированных типов атрибутов обсуждается более подробно ниже.

ЗАВОДСКИЕ МЕТОДЫ

Атрибут «style» используется для создания экземпляров представления определенного типа или «style». Большинство типов UIKit можно создать, вызвав новый метод для типа; однако некоторые типы требуют использования специального метода конструирования. Например,  UIButtonэкземпляры создаются с использованием  buttonWithType: метода  UIButton класса, а UITableView экземпляры инициализируются с помощью  метода initWithFrame:styleno-arg,  initкоторый вызывается  new.

Для обработки этих случаев MarkupKit поддерживает специальный атрибут с именем «style». Значение этого атрибута представляет имя «фабричного метода», метода с нулевым аргументом, который создает экземпляры данного типа. MarkupKit добавляет фабричные методы к классам, таким как  UIButton и,  UITableView чтобы позволить этим типам быть построенными в разметке.

Например, следующая разметка создает экземпляр «системного стиля»  UIButton:

<UIButton  normalTitle="Press Me!"/>

Обратите внимание на имя атрибута, который используется для установки заголовка кнопки. Стандартный UIButton класс не определяет свойство с именем  normalTitle, а определяет setTitle:forState: метод. Однако такие методы не совместимы с KVC и не могут быть вызваны напрямую через разметку. MarkupKit добавляет ряд свойств UIButton и других похожих классов, которые делегируют эти методы и позволяют настраивать их соответствующие типы в разметке.

СВОЙСТВА ШАБЛОНА

Часто при создании пользовательского интерфейса один и тот же набор значений свойств применяется повторно к экземплярам данного типа. Например, разработчик приложения может пожелать, чтобы все кнопки имели одинаковый внешний вид. Хотя можно просто дублировать определения свойств для каждого экземпляра кнопки, это повторяется и не позволяет легко модифицировать дизайн позже — каждый экземпляр кнопки должен быть расположен и изменен индивидуально, что может быть трудоемким и подверженным ошибкам ,

MarkupKit позволяет разработчикам абстрагировать общие наборы определений свойств в «шаблоны», которые затем можно применять по именам к экземплярам классов. Это значительно упрощает назначение общих свойств и их последующее изменение.

Шаблоны свойств определяются в файлах списка свойств (или  .plist ). Каждый шаблон представлен словарем, определенным на верхнем уровне списка свойств. Содержимое словаря представляет значения свойств, которые будут установлены при применении шаблона.

Шаблоны добавляются в документ MarkupKit с использованием инструкции по обработке свойств (PI). Например, следующий PI импортирует все шаблоны, определенные MyStyles.plist  в текущий документ:

<?properties MyStyles?>

Шаблоны применяются к экземплярам классов с использованием зарезервированного атрибута «класс». Значение этого атрибута относится к имени шаблона, определенного списком свойств. Все значения свойств, определенные шаблоном, применяются к экземпляру класса. Вложенные свойства, такие как titleLabel.font, поддерживаются шаблонами свойств.

Например, если  MyStyles.plist  определяет словарь с именем «label.greeting», который содержит следующие значения (для ясности сокращенно):

"label.greeting": {
    "font": "Helvetica 24"
    "textAlignment": "center"
}

следующая разметка создаст надпись «Hello, World!» в 24-точечной Helvetica с горизонтально-центрированным текстом:

<UILabel class="label.greeting" text="Hello, World!"/>

Несколько свойств PI могут быть указаны в одном документе. Их содержимое объединено в единую коллекцию шаблонов, доступных для документа. Если один и тот же шаблон определен несколькими списками свойств, содержимое шаблонов объединяется в один шаблон, и самые последние определенные значения имеют приоритет. Это позволяет документам разметки «переопределять» более глобально определенные стили локальными значениями.

МАГАЗИНЫ

Представления, определенные в разметке, сами по себе не особенно полезны. Зарезервированный атрибут «id» может использоваться для присвоения имени экземпляру представления. Присвоение представлению идентификатора определяет «выход» для представления и делает его доступным для вызывающего кода. Используя KVC, MarkupKit «внедряет» именованный экземпляр представления в владельца документа (обычно это либо контроллер представления для корневого представления, либо сам корневой просмотр), что позволяет коду приложения взаимодействовать с ним.

Например, следующая разметка объявляет экземпляр,  LMTableView содержащий UITextFieldLMTableView и  LMTableViewCell являются предоставляемыми MarkupKit подклассами  UITableView и  UITableViewCell, соответственно, которые упрощают определение содержимого статического табличного представления:

<LMTableView>
    <LMTableViewCell>
        <UITextField id="textField" placeholder="Type something"/>
    </LMTableViewCell>
</LMTableView>

Текстовому полю присваивается идентификатор «textField». Как и в Интерфейсном Разработчике, класс-владелец может объявить выход для текстового поля в Objective-C следующим образом:

@property (weak, nonatomic) UITextField *textField;

или в Swift, вот так:

weak var textField: UITextField!

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

АКЦИИ

Большинство нетривиальных приложений должны как-то реагировать на взаимодействие с пользователем. UIKit управляет (подклассы UIControl класса) событиями запуска,  которые уведомляют приложение, когда такое взаимодействие произошло. Например,  UIButton класс запускает UIControlEventTouchUpInside событие при нажатии на экземпляр кнопки.

В то время как приложение могло бы регистрировать события программным образом с использованием торговых точек, MarkupKit предоставляет более удобную альтернативу. Атрибут, имя которого начинается с «on» (но не совпадает с именем существующего свойства, имя которого равно или начинается с «on»), считается управляющим событием. Значение атрибута представляет собой имя действия, которое будет запущено при возникновении события. Имя атрибута — это просто префикс «on», за которым следует имя события, за исключением префикса «UIControlEvent».

Например, следующая разметка создает системный стиль,  UIButton который вызывает handleButtonTouchUpInside: метод владельца документа при нажатии кнопки:

<UIButton  normalTitle="Press Me!" 
    onTouchUpInside="handleButtonTouchUpInside:"/>

ПРИМЕР

Следующая разметка представляет собой простой пример многих из ранее обсужденных функций в действии:

<?xml version="1.0" encoding="UTF-8"?>

<?properties Styles?>

<LMColumnView layoutMargins="16" backgroundColor="#ffffff">
    <LMRowView layoutMargins="12" backgroundColor="#eeeeee">
        <UITextField id="nameField" placeholder="@name"/>
    </LMRowView>

    <UIButton  normalTitle="@sayHello" onTouchUpInside="showGreeting"/>

    <UILabel id="greetingLabel" class="label.greeting"/>

    <LMSpacer/>
</LMColumnView>

Первая строка после объявления XML импортирует список свойств с именем  Styles.plist , который определен следующим образом (снова сокращенно для ясности):

"label.greeting": {
    "font": "System-Bold 24"
    "textColor": #00aa00
    "textAlignment": "center"
}

Эти свойства используются для установки атрибутов метки, используемой для представления приветствия пользователю (с  UILabel именем «reetingLabel «в документе разметки). На этикетке будет использоваться жирный 24-точечный системный шрифт, и текст по центру будет отображаться в светло-зеленом цвете.

Корневой элемент документа является экземпляром  LMColumnView, подкласс  UIView которого автоматически упорядочивает свои подпредставления по вертикальной линии. Это один из нескольких классов, предоставляемых MarkupKit, которые помогают упростить разработку приложений, которые автоматически адаптируются к ориентации устройства или изменениям содержимого. В дополнение к  LMColumnViewMarkupKit также предоставляет  LMRowView, который упорядочивает подпредставления по вертикальной линии, и  LMLayerView, который упорядочивает подпредставления в слоях, как стопка прозрачных пленок.

Затем документ объявляет представление строки, содержащее,  UITextField которое позволяет пользователю вводить имя. Текстовому полю присваивается идентификатор «nameField», который создает выход для поля в контроллере представления. Выходы как для текстового поля имени, так и для метки приветствия объявляются в контроллере следующим образом:

weak var nameField: UITextField!
weak var greetingLabel: UILabel!

Атрибут «placeholder» текстового поля указывается как «@name». Это ссылка на ключ «name» в  файле Localizable.strings приложения  :

"name" = "Name";
"sayHello" = "Say Hello";
"unknownName" = "I don't know your name!";
"greetingFormat" = "Hello, %1$@!";

Когда документ загружен, это значение будет заменено фактическим строковым значением для ключа (т. Е. «Имя»).

Затем в документе объявляется экземпляр системного стиля  UIButton с заголовком в нормальном состоянии, который разрешается в «Say Hello». Нажатие на эту кнопку вызывает обработчик действия для UIControlEventTouchUpInside события кнопки  , который устанавливает значение метки приветствия, используя текст, введенный пользователем в поле имени:

func showGreeting() {
    let name = nameField.text
    let mainBundle = NSBundle.mainBundle()

    let greeting: String;
    if (name.isEmpty) {
        greeting = mainBundle.localizedStringForKey("unknownName", value: nil, table: nil)
    } else {
        greeting = String(format: mainBundle.localizedStringForKey("greetingFormat", value: nil, table: nil), name)
    }

    greetingLabel.text = greeting
}

Наконец, документ объявляет экземпляр  LMSpacer. Распорки — это пустые представления, которые увеличиваются, чтобы заполнить оставшееся пространство в родительском представлении. Размещение разделителя в конце представления столбца гарантирует, что содержимое столбца будет выровнено по верху столбца.

При запуске приложения выводится примерно следующее:

Пример разметки

РЕЗЮМЕ

В этой статье была представлена ​​структура MarkupKit и представлен обзор некоторых ее основных функций. Посетите   проект MarkupKit на GitHub для получения дополнительной информации.