В этой лекции мы рассмотрим работу с Autolayout. Autolayout — это система компоновки элементов интерфейса, основанная на констрейнтах. Она позволяет разработчикам создавать адаптивный пользовательский интерфейс, который нужным образом реагирует на внешние или внутренние изменения. То есть Autolayout динамически вычисляет размер и положение всех представлений в вашей иерархии View на основе определенных правил, заданных вами. Примерами внешних изменений могут быть изменения ориентации или размера экрана устройства, появление или исчезновение бара в верху приложения во время входящего звонка или записи звука. Примерами внутренних изменений могут быть динамическое изменение контента, отображаемого приложением, поддержка приложением нескольких языков или функции dynamic type, когда пользователь может изменять размер шрифтов во время работы приложения. Существует три основных подхода к разработке пользовательского интерфейса. Мы можем настраивать пользовательский интерфейс вручную в коде. Также можно воспользоваться механизмом Autoresizing mask для обработки некоторых внешних изменений либо использовать Autolayout. Первый подход основан на ручном выставлении начального положения и размера каждого View относительно родительского представления. Однако при любом изменении размеров или ориентации экрана вам придется пересчитывать положение и размер каждого View заново. Этот подход является, пожалуй, самым мощным, быстрым и гибким инструментом, однако даже для сравнительно простого интерфейса такой способ может оказаться очень трудоемким и сложным. Autoresizing mask позволяет облегчить процесс настройки процесса по сравнению с компоновкой вручную. Используя Autoresizing mask, можно указать, как будут меняться размеры View при изменении его superView. Это упрощает процесс создания интерфейса и обработку внешних изменений. Однако Autoresizing mask поддерживает сравнительно небольшое количество настроек для компоновки интерфейса и не может обработать внутренние изменения. Обычно при использовании Autoresizing mask для сложных интерфейсов комбинируется с ручной компоновкой. До появления Autolayout это было распространенной практикой. Autoresizing mask является предшественницей Autolayout. Следует заметить, что в современном SDK под капотом Autoresizing mask реализована на Autolayout. Если кратко, то принцип работы Autoresizing mask заключается в следующем. Вы указываете, какие края и какие размеры могут изменяться, а какие должны оставаться неизменными при изменении родительского View. Комбинируя эти настройки, мы получаем набор параметров, известных как Autoresizing mask. По сути, она представляет собой битовую маску, полученную путем применения побитового оператора ИЛИ к этим параметрам. Ознакомиться с этой темой более подробно можно, перейдя по ссылке в соответствующем разделе. Autolayout представляет собой совершенно иную парадигму. Вместо того чтобы думать о размерах каждого представления, вы думаете о том, как ваши представления связаны друг с другом. Одна связь между двумя какими-либо представлениями и есть констрейнт. Autolayout рассчитывает положение и размеры каждого View, основываясь на этих связях. В результате мы получаем интерфейс, который динамически изменяется в ответ на внешние или внутренние изменения. Давайте теперь более подробно поговорим о том, что из себя представляют констрейнты. Расположение ваших View в иерархии можно представить как набор линейных уравнений. Каждый констрейнт является одним из таких уравнений. Задача разработчика заключается в составлении такой группы уравнений, которая имеет одно и только одно возможное решение. Рассмотрим пример. Этот констрейнт устанавливает следующее. Передний край красного представления должен на восемь точек отстоять от заднего края синего представления. Теперь подробнее об элементах, представляющих констрейнт. Item 1 — первый объект, который должен быть либо View, либо Layout Guide. Attribute 1 — свойство первого элемента, на основе которого строится констрейнт. Relationship — отношение левой и правой стороны. Отношение может иметь одно из трех значений: равно, больше либо равно, меньше либо равно. Multiplier — число с плавающей точкой, на которое умножается значение Attribute 2. Item 2 — второй элемент, аналогичный Item 1. Attribute 2 — свойство второго элемента. Если второй элемент не установлен, это свойство должно иметь значение not an attribute. Пояснения будут даны чуть позже. Constant — константное смещение, выраженное числом с плавающей точкой. Констрейнты могут задавать отношения между двумя элементами интерфейса, как в примере ранее, либо между двумя свойствами одного элемента: например, устанавливать соотношения между высотой и шириной элемента. Также возможна установка постоянных значений для ширины или высоты элементов интерфейса. В этом случае Item 2 остается пустым, а Attribute 2, как было сказано ранее, получает значение non an attribute, и Multiplier имеет значение ноль. Все атрибуты делятся на два типа: атрибуты, задающие размер, например, height и width; и атрибуты, задающие положение, например, leading, trailing, top. Первые определяют, какого размера будет представление безотносительно его положения. Вторые задают положение представления относительно чего-либо. Полный список атрибутов можно найти по ссылке. Атрибуты положения могут указывать либо относительно реальных границ представления, либо относительно его Margins. Margins — это поля, или отступы, от края представления. Они обеспечивают визуальный зазор между контентом View и его реальной границей. По умолчанию margin равен восьми точкам относительно любой из сторон представления. Есть одна тонкость, о которой нельзя не упомянуть. Left и right констрейнты всегда будут относиться соответственно к левому и правому краю. Leading и trailing могут относиться к левому и правому краю для стран, в которых направление письма слева направо. И для стран, в которых направление письма справа налево, leading и trailing констрейнты будут зеркальны, то есть leading будет обозначать правый край, а trailing левый. Чтобы создавать интерфейсы с однозначным расположением элементов, нужно помнить правило — указывать как минимум два констрейнта на каждое измерение для элемента интерфейса: размер и позиция. На рисунке показан пример такой расстановки констрейнтов. Для простоты будем учитывать только горизонтальные констрейнты. Как видно, каждый лэйаут содержит один вид и два констрейнта, которые исчерпывающе задают ширину и горизонтальное положение вида. Поэтому каждый из трех лэйаутов обеспечивает однозначное расположение своего представления. По умолчанию все констрейнты, которые вы добавляете, являются обязательными. Это означает, что Autolayout при компоновке интерфейса будет искать такое их соотношение, которое удовлетворяет всем констрейнтам и обеспечит выполнение всех условий, заданных в них. Если Autolayout находит несколько таких соотношений, то возникает неоднозначность. И если Autolayout не может найти такое соотношение, он выдает ошибку. Возникает так называемый конфликт констрейнтов. Xcode показывает список конфликтующих между собой констрейнтов и предлагает убрать какой-то из них. После того как вы устраняете ошибку, Autolayout заново пересчитывает все констрейнты в поисках оптимального соотношения. Подробнее об этом можно прочитать в документации. Можно добавить необязательные констрейнты. Приоритет констрейнтов может изменяться в диапазоне от единицы до тысячи. Констрейнт с приоритетом 1000 является обязательным, все остальные необязательными. При выполнении расчета Autolayout сперва пытается найти соотношение, которое удовлетворяло бы всем констрейнтам, начиная от высшего приоритета к низшему. Если условие, заданное каким-либо необязательным констрейнтом, не может быть выполнено, этот констрейнт пропускается в Autolayout, и происходит переход к следующему констрейнту. Следует учесть, что даже если необязательно констрейнт был отброшен, он тем не менее может оказать влияние на итоговую компоновку интерфейса. Если после отбрасывания необязательного констрейнта в Autolayout возникает неоднозначность, система ищет такое решение, которое устранит неоднозначность и будет максимально близко к отброшенному констрейнту. Используя Autolayout, мы обычно добавляем констрейнты для определения положения и размера какого-либо представления. Однако некоторые представления имеют внутренний размер, который определяется их контентом. Это так называемый Intrinsic Content Size — размер, который описывает минимальное пространство, необходимое для полного отображения контента, например, внутренний размер UILabel или UIButton зависит от количества отображаемого ими текста и используемого шрифта. Для UIImageView внутренний размер будет определяться размерами изображения, которое оно показывает. Следует заметить, что не все представления имеют Intrinsic Content Size. Примером такого представления является UIView. У некоторых представлений Intrinsic Content Size может определять только высоту или только ширину представления, например, UISlider. Внутренний размер содержимого может быть получен через свойства UIView Intrinsic Content Size. Autolayout учитывает Intrinsic Content Size, создавая для каждого представления внутренние констрейнты для ширины и высоты. В отличие от других констрейнтов эти констрейнты имеют два приоритета: Content Hugging Priority и Content Compression Resistance Priority. Content Hugging Priority устанавливает приоритет, с которым View будет препятствовать увеличению своего размера относительно размеров своего контента. Грубо говоря, этот параметр показывает, насколько сильно View не хочет увеличиваться. Устанавливая большее значение этому приоритету, мы указываем, что не хотим, чтобы размер View увеличивался больше, чем размер своего контента, и наоборот. Другими словами, Content Hugging Priority показывает, насколько плотно представление будет охватывать свой контент. Content Compression Resistance Priority устанавливает приоритет, с которым View будет препятствовать уменьшению своего размера относительно размера своего контента, то есть насколько сильно View не хочет уменьшаться. Чем выше значение этого параметра, тем сильнее View препятствует своему сжатию и в конечном счете обрезке своего контента. Если по каким-то причинам вы не хотите использовать constraints, то можете использовать stack view. Stack view позволяет реализовать всю мощь Autolayout без использования constraints. Он представляет собой строку или столбец элементов пользовательского интерфейса. Stack view упорядочивает эти элементы на основе следующих свойств: Axis определяет ориентацию Stack view — горизонтальная или вертикальная. Distribution определяет расположение элементов в текущей ориентации Stack view. Alignment определяет расположение элементов перпендикулярно ориентации Stack view. Spacing определяет расстояние между соседними элементами. Stack view внутри себя использует Autolayout для упорядочивания своих элементов. Он совмещает первый и последний элементы со своими горизонтальными или вертикальными краями в зависимости от своего положения. Например, в горизонтальном Stack view первый край первого элемента цепляется к левому краю самого Stack view. Аналогично правый край последнего элемента цепляется к правому краю Stack view. Для позиционирования самого Stack view вам придется использовать constraints. Требуется закрепление по меньшей мере двух смежных краев Stack view для определения его положения. Без дополнительных constraints система вычисляет размер Stack view на основе размеров его элементов. Размер вдоль оси будет равен сумме соответствующих размеров элементов плюс сумма расстояний между ними. Размер перпендикулярно оси Stack view будет вычисляться как соответствующий размер самого большого элемента. Если же ширина и высота Stack view также заданы, он будет регулировать размеры и положение своих элементов для заполнения всего заданного пространства. Точное расположение и размер элементов Stack view будут зависеть от его свойств. Для более тонкой настройки вашего интерфейса можно использовать констрейнты внутри Stack view или вкладывать один Stack view в другой. В завершение нашей лекции мы расскажем о safe area. В iOS 7 Apple представила Top Layout Guide и Bottom Layout Guide как свойства UIViewController. Они устанавливали размер экрана, который не перекрывается другими представлениями, такими как Status Bar, Navigation Bar, Toolbar, Tab Bar и другими. Размещая констрейнты с учетом этих двух свойств, вы могли гарантировать, что ваш контент не будет перекрыт ничем другим при любой ориентации или любом размере экрана. Начиная с iOS 11, вместо Top Layout Guide и Bottom Layout Guide используется свойство Safe Area Layout Guide. Следует обратить внимание на следующее: Safe Area Layout Guide является свойством класса UIView, тогда как Top Layout Guide или Bottom Layout Guide — это свойства UIViewController. Но главное отличие в том, что с помощью Safe Area Layout Guide можно задавать не только верхние или нижние отступы, но и боковые. При этом цель у Safe Area та же — помочь разработчику разместить контент в видимой области интерфейса и обезопасить этот контент от наложения со стороны других представлений. На этом наша лекция завершена. Благодарю за внимание. [ЗВУК] [БЕЗ_ЗВУКА]