- PVSM.RU - https://www.pvsm.ru -
UI можно определить как набор вьюх, которые отображают данные, реагируют на события и при этом каким-то определенным образом расположены на экране.
Как мы размещаем элементы на экране?
We need to use code for special cases
Описанные инструменты заточены под типовые случаи, зачастую мы не можем (или можем, но это сильно неудобно) описать расположение какого-то элемента с помощью этих инструментов. Приходится делать это в коде. Логика описания layout'а размазывается по нескольким местам.
Должен быть способ лучше.
Layout is function
Что такое layout? Это описание расположения элементов. Его можно выразить в виде функции.
На вход функция получает размер контейнера и массив элементов, на выходе отдает координаты этих элементов.
Теоретически layout может быть любой – тут уж как дизайнер нарисует. Поэтому чтобы описать такую функцию нам нужен тьюринг-полный язык. UI фреймворки предлагают нам что угодно, только не тьюринг-полный язык, отсюда и проблемы. Логично было бы взять язык, на котором пишется остальная часть программы: для iOS – objc/swift, для android – java/kotlin и т.д.
We can easy describe layouts in code
Мы можем легко описывать layout в коде! Достаточно написать одну функцию. Часто мы будем располагать элементы относительно существующих (правее, ниже и т.д.). Можно спрятать такие вычисления в функции с читабельными именами. Под iOS это сделано в библиотеках Facade (objc) [1], Neon (swift) [2]. Можно легко написать свою на любом языке.
В iOS если вы хотите определить кастомный layout для вью, нужно переопределить метод layoutSubviews()
(или viewDidLayoutSubviews()
у контроллера) и задать в нем frame дочерним элементам. Этот метод вызывается при изменении размеров вью, то есть повороты, Split View и пр. зафорсят вызов этого метода.
Вот пример с официальной страницы Neon:
let isLandscape : Bool = UIDevice.currentDevice().orientation.isLandscape.boolValue
let bannerHeight : CGFloat = view.height() * 0.43
let avatarHeightMultipler : CGFloat = isLandscape ? 0.75 : 0.43
let avatarSize = bannerHeight * avatarHeightMultipler
searchBar.fillSuperview()
bannerImageView.anchorAndFillEdge(.Top, xPad: 0, yPad: 0, otherSize: bannerHeight)
bannerMaskView.fillSuperview()
avatarImageView.anchorInCorner(.BottomLeft, xPad: 15, yPad: 15, width: avatarSize, height: avatarSize)
nameLabel.alignAndFillWidth(align: .ToTheRightCentered, relativeTo: avatarImageView, padding: 15, height: 120)
cameraButton.anchorInCorner(.BottomRight, xPad: 10, yPad: 7, width: 28, height: 28)
buttonContainerView.alignAndFillWidth(align: .UnderCentered, relativeTo: bannerImageView, padding: 0, height: 62)
buttonContainerView.groupAndFill(group: .Horizontal, views: [postButton, updateInfoButton, activityLogButton, moreButton], padding: 10)
buttonContainerView2.alignAndFillWidth(align: .UnderCentered, relativeTo: buttonContainerView, padding: 0, height: 128)
buttonContainerView2.groupAndFill(group: .Horizontal, views: [aboutView, photosView, friendsView], padding: 10)
Неплохо для 10 строк кода, правда?
Часто UI описывается декларативно в виде разметки (xml, xaml, xib), которая потом превращается в код (Qt, Delphi, C# WinForms) или из которой создается готовый граф объектов (xib, xaml). При этом IDE предоставляют средства предпросмотра, что весьма удобно.
Описывая layout в коде мы отказываемся от инструментов предпросмотра.
Обычно layout идет сверху вниз: у нас есть размер экрана, зная этот размер мы layout'им первый слой вьюх, получив их размеры заходим глубже и т.д. Бывает нужно идти с другой стороны: растягивать контейнер по размеру содержимого. Описывать такое не всегда удобно.
На мой взгляд это хороший подход, если вы уже пишете UI в коде. Кода при этом получается примерно столько же как при использовании Autolayout, но работает он быстрее и предсказуемее.
Главный минус – теряется наглядность в виде инструментов предпросмотра.
Автор: zoooppy
Источник [5]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/ios-development/269244
Ссылки в тексте:
[1] Facade (objc): https://github.com/mamaral/Facade
[2] Neon (swift): https://github.com/mamaral/Neon
[3] LayoutFrameworkBenchmark: https://github.com/lucdion/LayoutFrameworkBenchmark
[4] PR: https://github.com/mamaral/Neon/pull/56
[5] Источник: https://habrahabr.ru/post/343318/?utm_source=habrahabr&utm_medium=rss&utm_campaign=sandbox
Нажмите здесь для печати.