Технология Auto Layout появилась в 2012 году, но споры и дебаты о том, как правильно верстать интерфейс, не утихают до сих пор. Использовать ли Auto Layout интерфейс в билдере или в коде? Верстать без него на фреймах или вообще использовать что-то стороннее? Тема такая горячая, что обсуждение докладов в кулуарах на эту тему часто проходит на повышенных тонах — у каждого есть свое мнение на этот счет. Сейчас, 8 лет спустя, вопрос всё ещё актуален, к тому же появились новые технологии и библиотеки для верстки.

На профессиональной конференции разработчиков мобильных приложений Apps Live 2020 был круглый стол с представителями Юлы, проектов VPROK и DeliveryClub. Мы обсудили все эти вопросы, а также быстрее и удобнее ли с AL или все-таки можно решать вопрос скорости другим способом.




Начал дискуссию Семен Мацепура из VPROK.

Семен Мацепура (vprok.ru): Сначала расскажу про наше приложение. Полгода назад мы приняли решение написать с нуля приложение конкретно под интернет-магазин. У нас был довольно сжатый срок — написать MVP примерно за 2 месяца, и мы решили сделать упор на архитектуру. Выбор пал на архитектуру RIBs, чтобы в дальнейшем мы могли поддерживать проекты, масштабироваться и расти.

При этом мы понимали, что на старте в MVP у нас будут более-менее легкие экраны с довольно легкой анимацией. Поэтому мы решили использовать Auto Layout без сторонних библиотек, потому что на тот момент это было более легким в поддержке, чем фреймы. И позволяло нам создавать легкие анимации в 2 строки кода.

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

Передаю слово своему коллеге Александру.

Александр Канчурин (vprok.ru): Дополню Семена по архитектуре. Команда у нас была разнородная. И было важно, чтобы каждый мог понимать, что происходит в любом месте кода. А с Auto Layout все были знакомы, было интуитивно понятно, как на нем верстать — будь то просто констрейнты в коде или же XIB файлы. К тому же с помощью Auto Layout можно лаконично верстать и сложные интерфейсы.

Да, проблемы с ним конечно же есть. Но не на всех экранах есть, например, проблемы с FPS. Когда мы получили на 2% экранов просадку по FPS, мы провели исследование внутри команды и выяснили, что Auto Layout не на первом месте среди этих проблем. Среди проблем были: подготовка данных для отображения и Kingfisher (на удивление), вместо которого мы собираемся использовать механизмы URLSession.

Да, может быть, там, где у нас действительно есть проблемы из-за Auto Layout, мы будем использовать фреймы, но это меньше 2% наших экранов. Поэтому пока мы не видим смысла переходить на фреймы — с Auto Layout все легко решается и всем все понятно.

Хорошо, это мы услышали одну позицию. А пока перейдем к Юле, у которой чуть более радикальный подход к разработке. Павел?

Павел Тихонов (Юла): Да, у нас более радикальный подход в том смысле, что мы вообще не используем Auto Layout. Он еще есть в проекте, потому что это куски legacy, но когда мы туда доберемся, мы его выпилим полностью.

Объясню, почему. Мы используем не голые frame, а сторонние библиотеки для удобной работы с ними. Две из них — это PinLayout и FlexLayout — обертки как раз над фреймами, которые позволяют декларативно объявлять весь интерфейс. Из-за этого, на начальном этапе у нас чуть-чуть выше порог вхождения, потому что человеку, как минимум надо разобраться с самой библиотекой. Но в будущем мы имеем код-ревью проще. Я смотрю код на Swift, а не открываю какой-нибудь XIB/Storyboard и не просматриваю xml.

Да, не спорю, можно использовать констрейнты в коде. Но нам без них проще жить — я не пытаюсь представить в голове, как эти констрейнты просчитаются, как разойдутся, разъедутся и т.д. Я просто читаю Swift и вижу, как объявлены view относительно друг друга, как они позиционируются — внутри за меня pin или flex посчитают фреймы, константы и т.д.

Тут еще вопрос в чем — у нас минимальное iOS, которое поддерживается — это 10.3, а это iPhone 5. А у нас, во-первых, много вариантов Layout, во-вторых, они все комплексные, сложные — одновременно списки товаров отображаются не только на первой вкладке, но имеется, как минимум, три вида отображения этих списков. И с этой точки зрения уход от системы именно этого движка дает нам прирост даже на Айфон 5.

Понятное дело, это не дает какого-то супер-плавного layout, который мы видим на iPhone 11 Pro (сейчас уже 12). Но сильно помогает сделать так, чтобы все-таки FPS не проседал, не фризился интерфейс и так далее.

Передаю слово моему коллеге Андрею Опарину.

Андрей Опарин (Юла): Я дополню про Auto Layout. Когда у нас действительно очень сложные вьюшки и нужно, например, что-то скрыть/что-то показать, то довольно сложно обновлять эти констрейнты. Это либо сразу несколько констрейнтов надо обновлять, либо высоту в 0 ставить, отступы убирать и все такое. И без Auto Layout мы по одной модели можем отрендерить, пролейаутить Layout в одном месте: что нужно — скрыли, что нужно — показали. Например, FlexLayout это очень хорошо позволяет делать.

Второй момент, что очень часто независимо от того, на чем мы верстаем, нам нужно в какой-то момент рассчитывать размеры. Наиболее часто мы хотим рассчитать высоту. Кто-то считает это относительно контента — например, высоту строки через bound rect. Я опишу наш подход на примере UI-label. Мы ставим какую-нибудь ширину и вызываем sizeToFit — и относительно этой ширины рассчитывается высота. Точно также мы можем сделать наоборот — установив высоту, вызвать sizeToFit, и у нас рассчитается ширина.

Такой же подход у нас к дереву вьюх. Мы можем взять опорную ширину, и в sizeToFit уже есть опорная ширина и начальная точка origin 0,0. Мы можем в одном методе Layout расставить все наши элементы относительно точки 0,0, относительно опорной ширины, залейаутить все наши элементы, взять max Y последнего, и получить высоту нашей вьюхи.

ОК. Перейдем к третьей команде. Это DeliveryClub. Ее представляет Кирилл. Кирилл, какие у вас подходы?

Кирилл Шаханский (DeliveryClub): У нас подход дефолтный, потому что, мне кажется, использование Auto Layout — это то, что приходит в голову в первую очередь. То, что мы пользуемся преимущественно нативными инструментами, нам помогает еще при найме новых сотрудников, иначе бы это было некой проблемой. То есть в целом я на стороне добра Auto Layout, мы его используем по максимуму, кроме очень сложных кейсов, как, например, transitions и, возможно, анимация.

Тогда следующий вопрос ко всем. Мы говорим, что да, Auto Layout срабатывает, но есть какие-то сложные экраны, где что-то идет не так. Вопрос: что является сложным экраном, как вы это замечаете, используете ли какие-то метрики? Или это просто визуально определяется? Опишите, что происходит в таком сложном случае и как вы действуете при этом.

Андрей Опарин (Юла): Для меня сложный экран, когда есть вьюха, у которой очень много состояний. И в зависимости от состояния что-то может скрываться, а что-то показываться. В этом случае обновлять констрейнты может быть неудобно или сложно, к тому же можно словить баги.

Александр Канчурин (vprok.ru): У нас классический пример сложного экрана — это коллекции внутри коллекций. Это горизонтальные списки, например, карточек продуктов в каком-нибудь каталоге. Но мы проводили измерения с помощью инструментов в Xcode и убеждались в том, что Auto Layout на последнем месте.

Кирилл Шаханский (DeliveryClub): Здесь опять же вопрос — да, AutoLayout на последнем месте, но насколько это отставание видно для пользователя? У нас iOS 12 (мы совсем слабые устройства не поддерживаем), но коллекции в коллекциях активно используются, и каких-то особых просадок не наблюдаем.

Александр Канчурин (vprok.ru): По нашему опыту, для пользователя очень сильно заметно было, когда мы неправильно данные подготавливали. После того, как подготовку данных изменили, стало намного лучше и лишь где-то иногда местами пролаги случаются. То есть, да, Auto Layout вместе со скрин-рендерингом на это сильно влияют. Поэтому мы хоть и топим за Auto Layout, но признаем, что у него есть проблемные места, и там, возможно, мы будем использовать и считать голые фреймы. К тому же это довольно просто получается.

А коллекции в коллекциях у вас прямо реюзаются? Например, есть ячейка с коллекцией — она прямо при реюзе проседает или как?

Кирилл Шаханский (DeliveryClub): У меня есть опыт в двух проектах с разными подходами. В текущем проекте при расчете таких вещей у нас каждая вьюха сама считает размер внутренней коллекции, а внешняя коллекция запрашивает у ячейки ее размер для конкретной модели. View может рассчитать свой размер на основании состояния, которое зашито во view-модель, и она его считает, условно говоря, ручками. Так как расстояние между своими элементами вьюха знает — тот же лейбл она считает через bound rect или через sizeToFit — то размер коллекции она тоже может знать.

Более интересно у нас было в предыдущем проекте, где мы использовали тоже нативный API systemLayoutSizeFitting. Там мы создавали вьюшку, конфигурировали ее с помощью view модели и накладывали констрейнты. И, в зависимости от направления скролла коллекции, если мы знаем фиксированные ширину (чаще всего) или высоту, systemLayoutSizeFitting достаточно быстро все считает и лагов не вызывает. Например, в случае коллекция в ячейке мы загружали данные, вызывали layoutIfNeeded, коллекция выставлялась и считался contentSize. И это было достаточно быстро.

Ещё вопрос к Юле. Павел говорил, что все еще есть legacy с Auto Layout. То есть в какой-то момент произошел переход от Auto Layout к новому подходу. Расскажи поподробнее, как это случилось и почему? Это произошло из каких-то лагов, проблем в приложении? Или вы просто решили, что так удобнее?

Павел Тихонов (Юла): Когда у нас был Auto Layout, у нас было много подходов. Мы использовали XIB’ы и сториборды, могли констрейнты расставлять руками (тогда еще не было якорей). Еще был Visual Format Language, когда описываешь строкой констрейнты, и потом они создаются. И, несмотря на мощные на тот момент девайсы, мы начали наблюдать появление лагов, подергиваний на нашей выдаче, а именно на список товаров — интерфейс становился комплексным, сложным. Помимо правильной верстки мы меняли и подготовку данных — часть в фон переводили, часть считали по-другому, что-то заранее просчитывали. После этого стало получше, но те же подёргивания как были, так и остались. Все это вместе подтолкнуло нас к тому, чтобы отказаться от Auto Layout и сказать: «Нет, спасибо, с нас хватит, мы перейдем на фреймы».

Кстати, известный iOS-разработчик Питер Штейнбергер писал в Твиттере, что если вы Bank of Amerika, то вы получите от UIKit, в том числе от Apple, дополнительные выставления setNeedsLayout/layoutIfNeeded в каких-то случаях. Это немного смущает, потому что движок Auto Layout снова начнет все эти расчеты, пересчеты и т.д., что может повлиять на перфоманс.

Хорошо. Давайте закончим с библиотеками Flex и Pin. Расскажите про порог входа? Насколько сложно адаптировать новых ребят, сколько уходит на это времени?

Андрей Опарин (Юла): PinLayout — это библиотека, чтобы руками фреймы не высчитывать, в ней легкие конструкции. Таких библиотек на GitHub, наверное, тысячи и в ней ничего особенного нет.

FlexLayout — да, тут порог входа повыше, потому что FlexLayout основан на кроссплатформенном фреймворке Yoga. FlexLayout напоминает верстку Swift UI — тоже на стеках, примерно также позиции рассчитываются. Есть, конечно, нюансы — например, нужно ставить приоритеты как в Auto Layout (сжатие-расширение). Это может по началу путать, сам иногда забываешь про это.

Внутри FlexLayout работает от листьев по сути. Он рассчитывает размеры всех вьюх тоже через sizeToFit (у которых есть внутренний размер). Потом идет наверх, все это собирает и расставляет все отступы. В результате мы там одним методом Layout получаем все рассчитанные фреймы.

Павел Тихонов (Юла): Но мы не наблюдали сложностей с порогом вхождения у ребят в команде — никому не приходилось разбираться днями, ночами и неделями. К тому же, если ты застреваешь на FlexLayout, то можно использовать его с PinLayout или даже вообще перейти только на него, он очень простой. У нас был случай, когда новый человек в команде за день собрал ячейку для коллекции, задав всего пару вопросов.

Андрей Опарин (Юла): Но проблемы есть и во Flex. У него есть некое неявное поведение. Например, когда у нас горизонтальный стек в вертикальном стеке, и мы в горизонтальный стек положили какой-нибудь лейбл и картинку, то в некоторых ситуациях он почему-то не ограничивает этот горизонтальный стек внешним. Это тоже может иногда путать. Но это, скорее, баги в самом Flex. Например, у каждой ноды есть свойство display. И если мы отключаем display, не отображая элемент, то FlexLayout не скрывает его, а просто фрейм в 0 ставит. Но если мы поставим фрейм в 0 у какой-нибудь кнопки, у нее внутри будет лейбл, и без script to bounce у нас будет этот лейбл отображаться. Проблем в FlexLayout не так много, но это тоже может запутывать.

Вы упомянули про Swift UI. Как вы считаете, решит ли это наши проблемы с Layout или только еще запутает сильнее?

Семен Мацепура (vprok.ru): Думаю, Swift UI в массы, скорее всего, придет только через пару лет, так как большинство проектов там поддерживает две версии ниже, чем актуальные iOS. Соответственно, до этого момента всё еще поменяется десять раз, и, возможно, довольно кардинально. Поэтому мы пока к нему присматриваемся и делаем это очень аккуратно.

Александр Канчурин (vprok.ru): Я бы еще добавил, что для нас переход к Swift UI будет равносилен переделыванию некоторых слоев архитектуры, а это повлечет за собой немалые последствия, поэтому нужно будет больше времени для разработки.

Кирилл Шаханский (DeliveryClub): У нас, наверное, аналогичная ситуация. Даже если мы поднимем версию, вряд ли мы будем активно на него переходить. Но кажется, SwiftUI может решить проблему и быстрой верстки, и быстрого обсчета. В стадии production Swift UI может стать стандартом де-факто.

Андрей Опарин (Юла): Я тоже пробовал Swift UI. Там классная, всегда валидная верстка. Единственное, что после последнего TTDC, когда обновили SDK, они добавили пару компонентов и поставили им iOS 14. Это обычная практика у них, но мы могли бы собрать новые элементы и для iOS 13. Вторая проблема у них с навигацией, то есть в случае present нескольких экранов, вызывая dismiss для закрытия двух или трех. То же самое с navigation-стеком. Как это нормально делает Swift UI, я пока не понял — но можно, например, пробрасывать какие нибудь флаги, для того, чтобы понять до какого экрана закрывать стэк.

Последний вопрос: Есть ли жизнь без Auto Layout?

Павел Тихонов (Юла): Конечно, она есть. И в Юле можно наблюдать это каждый день. И, да, жизнь без AutoLayout не становится сложнее, больнее, хуже или еще как-то. Местами, она становится даже проще. А если говорить про поддержку прошивок и девайсов пятилетней давности, то и плавнее.

Работа над программой докладов весенних конференций уже идёт: 22 декабря Программный комитет FrontendConf встречался онлайн со спикерами, а в пятницу, 25 декабря, в 19:00 состоится встреча докладчиков и ПК DevOpsConf.
Подключайтесь сами или перешлите своим докладчикам!

Для 2021 года мы подготовили 14 конференций, в том числе и перенесенных с этого года. Смотрите план наших конференций на весь 2021 год с датами закрытия Call for Papers.
Изучайте, подбирайте конференцию для себя!