На iOS, каковы различия между полями, кросс-вставками, вставками содержимого, выравниванием прямоугольников, макетами макета, привязками...?

Кажется, существует несколько различных вариантов/терминов и людей в сообществе iOS в отношении макета (например, UIEdgeInsets - это тип, но иногда я слышу/читаю "устанавливаю вставки" или макеты макета против макетов).

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

Может ли кто-нибудь помочь обеспечить определенную ясность между этими различными аспектами макета и когда использовать их наилучшим образом?

Ответ 1

Будучи предложителем Баунти... Я бы сказал, что большая часть моего замешательства возникла из-за неправильного понимания класса UILayoutGuide. Это ключевой момент, но также очень простой.

Позвольте мне сначала представить проблему:

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

enter image description here

Затем вам нужно было создать чистые UIViews и добавить их в качестве ваших подпредставлений, а затем добавить к ним свои ограничения, как показано ниже:

enter image description here


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

Руководства по макету

Чтобы создать руководство по макету, необходимо выполнить следующие действия:

  1. Создание нового руководства по макету.
  2. Добавьте руководство по макету в представление, вызвав метод views addLayoutGuide(_:).
  3. Определите положение и размер руководства по макету, используя Auto Layout. Вы можете использовать эти направляющие для определения пространства между элементами в макете. В следующем примере показаны направляющие макета, используемые для определения равного расстояния между сериями видов.

шаги:

let space1 = UILayoutGuide()
view.addLayoutGuide(space1)

let space2 = UILayoutGuide()
view.addLayoutGuide(space2)

space1.widthAnchor.constraintEqualToAnchor(space2.widthAnchor).active = true
saveButton.trailingAnchor.constraintEqualToAnchor(space1.leadingAnchor).active = true
cancelButton.leadingAnchor.constraintEqualToAnchor(space1.trailingAnchor).active = true
cancelButton.trailingAnchor.constraintEqualToAnchor(space2.leadingAnchor).active = true
clearButton.leadingAnchor.constraintEqualToAnchor(space2.trailingAnchor).active = true

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

Три интересные заметки:

  1. Если вы используете "просмотр иерархии отладки", вы увидите больше экземпляров UILayoutGuide
  2. Как и в UIView, экземпляр UILayoutGuide имеет все виды якорей
  3. Что касается того, почему не просто создать фиктивные UIViews и пройти через создание UILayoutGuides: "Существует несколько затрат, связанных с добавлением фиктивных представлений в вашу иерархию представлений. Во-первых, это стоимость создания и поддержки самого представления. Во-вторых, фиктивное представление является полноправным членом иерархии представления, что означает, что он добавляет издержки к каждой задаче, которую выполняет иерархия. Хуже всего то, что невидимое фиктивное представление может перехватывать сообщения, предназначенные для других представлений, вызывая проблемы, которые очень трудно найти ".

enter image description here

Для получения дополнительной информации см. документацию.


topLayoutGuide против safeAreaLayoutGuide

topLayoutGuide (устарело)

Не рекомендуется, но для целей обучения: у UIViewController есть 2 пустышки. 1 свойство вверху с именем topLayoutGuide и другое свойство внизу с именем bottomLayoutGuide. Сам ViewController не имеет направляющих для левой/передней или правой/задней сторон. Оба они являются экземпляром UILayoutGuide

если ограничен view.topAnchor, т.е.:

tableView.topAnchor.constraint(equalTo: view.topAnchor)

enter image description here tableView не начинается с нижней части панели навигации. Обратите внимание на оранжевый цвет за панелью навигации...

Однако, если вы ограничили его topLayoutGuide.bottomAnchor, т.е.

tableView.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor) 

enter image description here

затем tableView начинается с нижней части панели навигации

И в зависимости от вашего макета вы можете захотеть, чтобы ваш контент был размытым под панелью навигации.

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

Подробнее см. этот момент из WWDC и этот вопрос здесь. Я не думаю, что решения точно связаны, только изображение в вопросе.

safeAreaLayoutGuide

с iOS11

Apple устарела topLayoutGuide & bottomLayoutGuide. Так вместо двух пустышек теперь у вас есть пустышка с именем safeAreaLayoutGuide в экземпляре UIView. У UIViewController больше нет этого... Визуальное сравнение скопировано из useyourloaf: enter image description here

примечание: если вы используете раскадровки, то выравнивание ваших представлений с topLayoutGuide или top из safeAreaLayoutGuide будет отображать то же самое. Если вы не используете раскадровки (делайте это программно), вам придется танцевать между iOS11 и LessThaniOS11 и иметь 2 разные версии кода

Для получения дополнительной информации о safeAreaLayoutGuide я настоятельно рекомендую установить статью Apple на: Позиционирование контента относительно безопасной зоны

ПРИМЕЧАНИЕ. safeAreaLayoutGuide - это свойство UIView. topLayoutGuide является свойством UIViewController.


layoutMarginsGuide

  • UIView имеет только 1 пустышку. Свойство называется layoutMarginsGuide. Но в отличие от UIViewController он не сидит сверху или снизу. Он просто расположен в центре с 8-точечным заполнением/вставкой (со всех четырех сторон) в UIView.Итак, где это полезно?: Вы бы использовали это, если вы не делаете хотите, чтобы ваш textView был ограничен краями экземпляра UIView. Это улучшит опыт чтения. Или вместо того, чтобы ограничить кнопку ведущим якорем ее суперпредставления и сделать его уродливым, вы добавляете 8 ячеек к якору... то есть ограничиваете кнопку в ведущем якоре и затем добавляете 8 постоянных точек. Выделенный текст, на самом деле, вы бы использовали readableContentGuide, layoutMarginsGuide полезно, если вы не хотите, чтобы ваша кнопка или метка были прикреплены к краю суперпредставления

    someButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 8)
    

    Но подождите, есть более простой способ. Просто используйте рекомендованную Apple маржу, т.е. используйте:

    someButton.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor)
    

    Также см. пример, приведенный в документация. Хороший учебник Raywenderlich можно найти здесь


readableContentGuide

  • Немного отличается от layoutMarginGuide. Оба являются свойствами UIView. Иногда они идентичны, иногда нет. Это цель:

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

    Подробнее см. этот момент из WWDC: создание адаптивного макета и это замечательное руководство по использованию youyloloaf.

    На iPhone 7 Plus в портретной ориентации читаемые руководства по содержанию одинаковы как ориентиры полей зрения, но в ландшафте есть больше белого пространство по обе стороны от текстового представления. На iPad в альбомной ориентации пробел значительно увеличен.

    Размер поля зависит от динамического типа системы. Чем больше шрифт, тем шире будет руководство.

    From RayWenderlich

На изображении ниже голубой привязан к layoutMarginGuide, но зеленый привязан к readableContentGuide:

enter image description here


UIEdgeInsets

Если вы хотите изменить layoutMarginsGuide, то есть изменить желаемое поле с 8 до 16 баллов, вы должны изменить значение layoutMargins, и якоря layoutMarginsGuide будут автоматически обновлены. UIEdgeInsets это просто тип вашего layoutMargins. layoutMargins - это имя свойства класса UIView

someview.layoutMargins = UIEdgeInsets(top: 50, left: 50, bottom: 50, right: 50)

Единственное место, где я нашел этот код ☝️, чтобы он действовал, находится внутри viewDidLayoutSubviews. Подробнее см. здесь


Anchors

Они основополагающие, но ничего особенного. Они являются самым дальним краем любого UIView/UILayoutGuide. И экземпляры UIView и UILayoutGuide имеют это. ВСЕ, которое вы ограничиваете, в конечном итоге ограничивается использованием якорей, это просто вопрос того, к каким объектам якоря вы привязываете. Это может быть якорь safeAreaLayoutGuide, это может быть якорь layoutMarginGuide, это может быть якорь topLayoutGuide, это может быть якорь view. (хотя вы также можете просто привязать свой heightAnchor к 50, так что в этом случае другого якоря не будет)

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


contentInsets

Хотя важно понимать, это совершенно другое обсуждение и не имеет ничего общего с другими. Это специфично для UIScrollViews. Подробнее читайте в этой замечательной статье


Заключение

Чтобы быть в безопасности и быть уверенным, что все внутри вашего зрения, используйте safeAreaLayoutGuide. Если вы хотите использовать предоставленные системой поля, чтобы иметь лучшее расположение видов или иметь некоторые отступы, используйте layoutMarginGuide, если вы хотите сделать вещи более читабельными readableContentGuide.

Размер readableContentGuide всегда меньше или равен layoutMarginGuide.
Размер layoutMarginGuide всегда меньше или равен safeAreaLayoutGuide

layoutMargins очень похож на заполнение CSS. safeAreaLayoutGuide похож на CSS поля. Я не знаю, есть ли какой-нибудь CSS-эквивалент для readableContentGuide


Приложение: ContentInset против contentOffset

Они предназначены для scrollViews и в некоторой степени не связаны с остальной частью ответа. Что касается того, что contentInset & contentOffset, смотрите этот момент из WWDC 2018: UIKit: приложения для любого размера и формы . Видео очень простое. также см. ответ Karthik ниже. При этом важно, чтобы вы полностью понимали, как работает scrollView, и понимали, что такое contentSize, иначе это будет сложно. Подробнее о contentSize и scrollView см. ответ Vacawama здесь

Ответ 3

Извините, если это скучный ответ, но я чувствую, что документация Apple Developer неплохо описывает, как будет использоваться каждое свойство UIView.

Что касается наилучшего способа реализации макета, когда у вас есть несколько методов/параметров, которые работают... это действительно вопрос стиля/мнения. Я бы остановился на следующих критериях, чтобы прийти к решению:

  • Рассмотрите возможность устранения параметров, которые наиболее сложно поддерживать/отлаживать/понимать
    • Представьте, что кто-то новый присоединился к вашей команде или кому-то, унаследовавшему ваш код, - в какой реализации им будет сложно отладить и почему?
  • Рассмотрите возможность устранения параметров, затрудняющих компоновку переупорядочения/расширения/редактирования
  • Рассмотрите возможность устранения параметров, которые не соответствуют остальной части приложения (этот вид возвращается к первой точке)
  • Рассмотрите возможность устранения вариантов, которые идут против намерения Apple (см. переговоры по документам /WWDC, чтобы понять их намерения).

Команда могла бы работать вместе по этим критериям, чтобы договориться о том, как мы планируем наш пользовательский интерфейс в каждом сценарии. Я предполагаю, что вы просите - это суммарный итог таких обсуждений: макет-стиль.

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


Некоторые отрывки из документации Apple для классов, описанных в вопросе:

Вкл UILayoutGuide:

Используйте руководства по компоновке для замены созданных вами фиктивных представлений для представления пространств промежуточного представления или инкапсуляции в вашем пользовательском интерфейсе. Традиционно существует ряд методов автоматического компоновки, требующих фиктивных представлений.

...

Класс UILayoutGuide предназначен для выполнения всех задач, ранее выполнявшихся фиктивными представлениями, но для более безопасного и эффективного использования.

Вкл layoutMargins:

В iOS 11 и более поздних версиях используйте свойство directionalLayoutMargins для указания полей макета вместо этого свойства.

Вкл directionalLayoutMargins:

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

Вкл contentInset:

Используйте это свойство, чтобы расширить пространство между вашим контентом и краями содержимого. Единица измерения - это точки. Значение по умолчанию - UIEdgeInsetsZero.

Вкл topAnchor:

Используйте этот якорь для создания ограничений с верхним краем представления. Вы можете объединить этот якорь только с другими якорями NSLayoutYAxisAnchor. Для получения дополнительной информации см. NSLayoutAnchor.

Вкл NSLayoutAnchor

Используйте эти ограничения для программного определения макета с помощью автоматической компоновки. Вместо того, чтобы напрямую создавать объекты NSLayoutConstraint, начните с объекта UIView, NSView или UILayoutGuide, который вы хотите ограничить, и выберите один из свойств привязки объектов. Эти свойства соответствуют основным значениям NSLayoutConstraint.Attribute, используемым в Auto Layout, и предоставляют соответствующий подкласс NSLayoutAnchor для создания ограничений для этого атрибута. Используйте методы привязки для создания ограничения.