Добро пожаловать во вторую часть серии статей по AsyncDisplayKit (Texture)! Система компоновки AsyncDisplayKit позволяет писать невероятно быстрый, декларативный код. Помимо быстрой настройки, она автоматически адаптируется к устройству, на котором запущено приложение. Допустим, вы пытаетесь создать узел, который можно использовать во view controller приложения или в качестве popover в приложении для iPad. Если его layout будет правильно создан, вы сможете перенести узел в эту новую среду, не беспокоясь об изменении базового кода макета!  В этом туториале AsyncDisplayKit 2.0 вы вернетесь к классу CardNode из первой части и узнаете о спецификациях layout, которые были использованы для его создания. Вы увидите, как легко составлять спецификации макета, чтобы получить желаемый результат. Я слышу, как вы кричите: «Что не так с Auto Layout ?!» В Auto Layout каждый создаваемый вами коснтрейнт представляется в виде уравнения в системе уравнений. Это означает, что каждый добавленный констрейнт увеличивает время расчета констрейнтов экспоненциально. Такой расчет всегда выполняется в основном потоке. Одна из целей дизайна ASDK – как можно лучше придерживаться API UIKit. К сожалению, Auto Layout – непрозрачная система, в которой нет способа выполнять расчет констрейнтов в другом потоке. Для начала загрузите стартовый проект отсюда. Поскольку вы будете изучать часть спецификаций макетов, стоит начать с измененной версии готового продукта из первого туториала по AsyncDisplayKit 2.0. Прежде чем начать, расскажу вам немного предыстории. Спецификации макета – это обобщение системы макетов, о которой вкратце рассказано в Building Paper Event. Идея состоит в том, чтобы унифицировать вычисление и применение размеров и положений узла и подузлов, и иметь возможность их переиспользовать. В ASDK 1.9.X вы могли создавать асинхронные макеты, но код макета был похож на pre-auto layout в UIKit. Размер подузлов узла можно было вычислить в методе -calculateSizeThatFits :. Эти размеры могли быть закэшированны, а затем добавлены позже в -layout. А позиции узлов все еще должны были рассчитываться с использованием старой-доброй математики – никто не любит возиться с математикой.  В ASDK 2.0 подклассы ASDisplayNode могут реализовать -layoutSpecThatFits :. Объект класса ASLayoutSpec определяет размер и положение всех подузлов. При этом спецификация макета также определяет размер упомянутого родительского узла. Узел вернет объект спецификации макета из -layoutSpecThatFits :. Этот объект определит размер узла, а также размеры и позиции всех его подузлов рекурсивно. Аргумент ThatFits является объектом класса ASSizeRange. Он имеет два свойства типа CGSize (min и max), которые определяют наименьшие и наибольшие размеры узла. ASDK предоставляет множество различных видов спецификаций. Вот некоторые из них: Спецификации макета управляют расположением одного или нескольких дочерних элементов. Элемент может быть узлом, таким как ASTextNode или ASImageNode. В дополнение к узлам, дочерний шаблон спецификации макета также может быть другой спецификацией макета. Эй, как это возможно? Дочерние элементы спецификации макета должны соответствовать протоколу ASLayoutElement. ASLayoutSpec и ASDisplayNode соответствуют ASLayoutElement, поэтому оба типа и их подклассы могут быть дочерними элементами.  Эта простая концепция оказывается невероятно мощной. Одной из наиболее важных характеристик макета является ASStackLayoutSpec. Возможность разместить изображение и текст – это одно, а вот разместить изображение и другой стек – совсем другое! Вы совершенно правы. Пришло время дуэли! Я имею в виду, писать код ...  Итак, вы на работе, и ваш дизайнер отправил вам скриншот того, что он хочет для нового приложения «Энциклопедия для животных», над которым вы работаете.  Первое, что нужно сделать, это разбить экран на соответствующие спецификации макета, чтобы понять общий макет. Иногда это может ввести в ступор, но помните, что мощность макета зависит от того, насколько легко они могут быть скомпонованы. Начните с простого. Забегая вперед, скажу лишь, что верхняя и нижняя половины будут отлично работать в стеке вместе. Теперь, когда вы это знаете, можете разместить две половинки отдельно и объединить их в конце. Разархивируйте стартовый проект и откройте RainforestStarter.xcworkspace. Перейдите в CardNode.m к методу -layoutSpecThatFits :. Сейчас он просто возвращает пустой объект ASLayoutSpec. Если вы скомпилируите и запустите проект, то увидите следующее:  Хорошо, это только начало. Как насчет того, чтобы сперва показать изображение животного? По умолчанию узел сетевого изображения не имеет содержимого и, следовательно, не имеет собственного размера. Посмотрев на скриншот, можно сказать, что изображение животного должно занимать полную ширину экрана и 2/3 высоты. Для этого замените существующий оператор return следующим: Рассмотрим каждый пронумерованный комментарий по очереди: Скомпилируйте и запустите, чтобы посмотреть, как выглядит ваш макет:  Довольно легко, да? Поскольку изображение – единственный элемент, у которого есть размер, ячейки растянулись, чтобы приспособиться к нему. Теперь, когда у вас есть изображение животного, следующим логическим шагом является добавление поверх него градиентного узла.  ASOverlayLayoutSpec – это просто спецификация для работы. Сначала добавьте следующую строчку после инициализации imageRatioSpec: При создании своих спецификаций макета вы всегда получите те, которые содержат остальные. Пришло время для gradientOverlaySpec. Замените текущий return следующим. Скомпилируйте и запустите, чтобы увидеть градиент, растянутый по каждому объекту imageNode. Градиент для каждой птицы – отлично!  Единственное, что осталось сделать – это добавить название животного. Хотя задача кажется простой, есть несколько требований, которые необходимо учитывать: Вы уже знаете, как разместить текст сверху. Пришло время вырваться из проверенной и истинной спецификации оверлея. Добавьте следующую строчку сразу после gradientOverlaySpec Кроме того, вам нужно изменить оператор return на следующий: Скомпилируйте и запустите приложение, чтобы увидеть текст на экране:  Неплохо. Вам просто нужно переместить этот текст в нижний угол. Стоит упомянуть об общем случае, с которым вы столкнетесь. У вас есть текст на птице, поэтому естественное стремление – обернуть OverlaySpec в другие спецификации, чтобы поместить его там, где хотите. Как правило, нужно сделать шаг назад и подумать о том, что вы пытаетесь выразить. В этом случае вы используете nameOverlaySpec, чтобы расширить что-нибудь еще поверх существующего содержимого. На самом деле вы не хотите расширять название по содержанию. Вы лишь хотите сказать имени, что оно должно быть в нижнем левом углу своего свободного пространства, а затем растянуть эту спецификацию макета по доступному пространству. На самом деле вам нужен ASRelativeLayoutSpec. ASRelativeLayoutSpec принимает дочерний объект ASLayoutElement, рассматривает пространство, в котором оно доступно, и затем помещает этот дочерний элемент в соответствии с вашими инструкциями. При определении относительной спецификации, можно установить ее свойства verticalPosition и horizontalPosition. Эти два свойства могут быть следующими: Комбинация позволяет разместить объект в одном из углов, краев или в центре доступного пространства. В качестве упражнения: как бы вы поместили эту лягушку на правом краю свободного пространства?  Если вы сказали: «Установите verticalPosition в ASRelativeLayoutSpecPositionCenter и horizontalPosition в ASRelativeLayoutSpecPositionEnd», то вы правы! Теперь, когда вы попрактиковались, следующая строка должна иметь немного больше смысла. Добавьте эту строчку прямо перед nameOverlaySpec: Так вы установите horizontalPosition дочернего элемента для старта и verticalPosition для завершения. На лягушачьем языке это выглядело бы примерно так:  Теперь, когда у вас установлена ​​относительная спецификация, измените определение nameOverlaySpec на следующее: Скомпилируйте и запустите, чтобы посмотреть, что у вас получилось:  Хорошо! Только есть еще одна вещь, которую нужно сделать на этой половине ячейки. Последнее, что вам нужно сделать, это поместить название животного на 16 поинтов слева и на 8 поинтов вниз. Для этого у вас есть ASInsetLayoutSpec. Чтобы добавить небольшой отступ вокруг любого из ваших объектов, просто оберните объект в спецификацию вставки и предоставьте UIEdgeInsets определить, какой отступ вам нужен. Добавьте следующую строчку после nameOverlaySpec: Затем еще раз измените оператор return, чтобы вернуть самую внешнюю спецификацию. Скомпилируйте и запустите.   Вы не хотите, чтобы вставка была применена ко всей области, которую охватывает оверлей, так как она включает в себя изображение вашего животного. То, что вы на самом деле хотите, это применить вставку к пространству relativeSpec. Чтобы исправить это, сначала удалите текущее определение nameInsetSpec. Затем добавьте следующую новую и улучшенную версию прямо перед определением nameOverlaySpec: Теперь вам нужен nameOverlaySpec, чтобы наложить новую вставку, не relativeSpec. Замените определение nameOverlaySpec на: Наконец, вернитесь к: Теперь скомпилируйте и запустите, чтобы увидеть что получилось:  Половина работы выполнена! Вторая половина немного легче. Это просто описание животного со вставкой вокруг него… и вы уже знаете, как это сделать. Добавьте следующую строчку перед оператором return, чтобы создать вставку с текстом описания. Если бы вы вернули эту вставку, а затем скомпилировали и запустили приложение, то увидели бы следующее:  Это именно то, что вы хотели. Теперь, когда мы разобрались с обеими половинками, объединить их вместе совсем несложно. Вам не нужно беспокоиться о том, чтобы у текста был размер контента для заполнения пространства. Это потому, что ASTextNode имеет свой внутренний размер содержимого, основанный на его тексте и атрибутах. Следующие узлы не имеют дефолтного размера: Общность заключается в том, что у этих узлов нет первоначального контента и, следовательно, нет способа определить их собственный размер. Они должны либо иметь preferredFrameSize, либо быть помещены в спецификацию макета, прежде, чем у них будет конкретный размер для работы. Это идеальное время для использования спецификации макета стека. Вы можете воспринимать это как спецификацию макета, эквивалентную UIStackView, исключая ее автоматическую обратную совместимость, которая довольно изящна. Стеки могут быть определены как вертикальные, так и горизонтальные. И как все, спецификации макета могут принимать либо узлы, либо другие разметки в качестве дочерних элементов. Чтобы настроить этот стек, добавьте эти три строки после определения вставки описания: Здесь вы создаете стек, устанавливая его направление вертикальным и добавляя верхнюю половину и нижнюю половину в качестве дочерних элементов. И снова верните новую спецификацию макета. Скомпилируйте и запустите. Почти готово!  Вложенность стеков, каждый из которых имеет свои собственные настройки justifyContent и alignItems, означает, что они могут быть невероятно выразительными, а также невероятно разочаровывающими. Не забудьте посмотреть flex box froggy game и Async Display Kit docs для более глубокого изучения. Помните спецификацию оверлея? Одно из правил заключается в том, что предмет, который накладывается на нее, должен иметь свой собственный размер. Элемент сзади определяет размер, а элемент спереди просто растягивается над ним. Фоновая спецификация в точности противоположна. Если у вас есть один элемент, который может определить свой собственный размер, а другой вы хотите растянуть за ним, понадобится спецификация фона. В этом случае вам нужно использовать спецификацию макета фона, чтобы растянуть изображение размытого животного. Для этого добавьте следующую строчку: И замените оператор return  Скомпилируйте и запустите, чтобы посмотреть, что получилось:  Завершенный проект доступен по ссылке. Повторюсь, что также есть версия для Swift. Как только вы разберетесь с этими концепциями, начните изучать документацию. Это был просто маленький обзор того, на что способна система компоновки. Мы надеемся, что вам понравился этот туториал AsyncDisplayKit 2.0, и если у вас есть вопросы – не стесняйтесь оставлять их в комментариях! P.S. Отдельное спасибо BeataSunshine и evgen за помощь в переводе статьи.