UITableViewCell с автозапуском левого края отличается от iPhone и iPad
Я использую сгруппированный UITableView
со статическими ячейками для экрана/сцены параметров. Все делается в Xcode 6.1/iOS 8.1.x/Storyboard, используя Autolayout. Внутри групп таблицы есть смешанные типы ячеек, и есть два типа, которые вызывают у меня проблемы:
- Ячейки с пользовательским стилем и
- Ячейки со стилем "Правая деталь"
В ячейке # 1 я могу установить ограничение для левого поля между меткой и ведущим контейнером. Насколько я знаю, на ячейке №2 я не могу установить никаких ограничений в Interface Builder. Я установил левое поле на ярлыке в ячейке # 1, чтобы он выравнивался с меткой в ячейке # 2. Все выглядит отлично на iPhone, но если я покажу ту же таблицу на iPad, где размер контейнера представления таблицы равен половине размера экрана, ячейка №2 получает больше разницы (динамически?), В то время как ячейка № 1 поддерживает абсолютный запас я заданных в ограничениях. Я также попытался изменить левое поле в ячейке # 1 с атрибутом "относительно поля", но безрезультатно.
iPhone:
![Table on iPhone]()
iPad (с шириной экрана tableview = 1/2)
![Table on iPad]()
Итак, вопрос: как установить ограничения для метки в ячейке # 1 так, чтобы она выравнивалась как ячейка # 2.
Вот также ссылка на образец Xcode 6.1, демонстрирующий проблему. Запустите iPhone и iPad, чтобы увидеть разницу:
https://dl.dropboxusercontent.com/u/5252156/Code/tableViewTest.zip
Этот вопрос может быть связан с ячейкой статической таблицы компоновки для iPhone и iPad, но он может также отличаться для iOS 8, поскольку теперь все должно быть адаптивным. Поэтому я решил отправить этот вопрос в любом случае.
Ответы
Ответ 1
Как исправить его
После борьбы с командой отчетов об ошибках Apple со множеством примеров проектов и скриншотов и анализом который отвечает, я обнаружил, что решение иметь ваши ячейки пользовательского стиля вести себя последовательно относительно их маржи и быть таким же, как UITableViewCells по умолчанию, вы должны сделать следующее (в основном на основе ответа Бекки, я подсказал, что другое и что сделало его работа для меня):
- Выберите вид вашей ячейки в IB
- Перейдите к Инспектору размеров
-
В разделе "Поля макета" установите флажок "Сохранять поля надзора" (не нажимайте знак "плюс" )
![Проверка полей сохранения супервизора]()
-
(И вот этот ключ). Сделайте то же самое для самой ячейки (родительский контент, если вы это сделаете)
![Ячейка, а не просмотр содержимого на этот раз]()
-
Задайте свои ограничения следующим образом: Label.Leading = Superview.Leading Margin (с константой 0)
![Установка ограничения для метки пользовательской ячейки]()
Теперь все ваши ячейки будут иметь свой ярлык, соответствующий ячейкам по умолчанию! Это работает для меня в Xcode 7 и выше, и оно включает исправление, упомянутое в потоке, о котором я упоминал. IB и симулятор должны теперь отображать правильно выровненные метки.
![Конечный результат в симуляторе]()
Вы также можете сделать это программно, например, в классе View Controller:
cell.preservesSuperviewLayoutMargins = true
cell.contentView.preservesSuperviewLayoutMargins = true
Или вы могли бы настроить его, вызвав UIAppearance один раз при запуске (я знаю только Swift, извините):
UITableViewCell.appearance().preservesSuperviewLayoutMargins = true
UITableViewCell.appearance().contentView.preservesSuperviewLayoutMargins = true
Как и почему это работает
Как Этан любезно отметил, что собственная документация Apple по UIView описывает preservesSuperviewLayoutMargins
следующим образом:
Когда значение этого свойства равно true
, поля прокрутки также учитываются при размещении содержимого. Эта маржа влияет на макеты, где расстояние между краем представления и его надзором меньше соответствующего поля. Например, у вас может быть представление контента, чей фрейм точно соответствует границам его супервизора. Когда какой-либо из полей супервизора находится внутри области, представленной представлением содержимого и его собственными полями, UIKit настраивает макет представления контента, чтобы уважать поля супервизора. Размер корректировки - это наименьшая сумма, необходимая для обеспечения того, чтобы содержимое также находилось внутри полей супервизора.
Поэтому, если вы хотите, чтобы содержимое вашей ячейки совпадало с полями TableView (это отличное дедушка, если хотите), вам нужно иметь в своем содержании два восходящих канала, Content View и сам Table Cell, сохраняют поля собственного наблюдения.
Почему это не поведение по умолчанию меня удивляет: я чувствую, что большинство разработчиков, которые не хотят настраивать все, ожидали бы это "наследование" по умолчанию.
Ответ 2
Я столкнулся с той же проблемой, что и вы, и придумал решение.
Во-первых, небольшой фон: поскольку iOS 8, ячейки таблицы по умолчанию рассматривают ячейку layoutMargins
для адаптации к различным признакам (например, к экранам aka devices). Например, макеты макета на всех iPhone (кроме iPhone 6 Plus, если они показаны на листе формы) составляют {8, 16, 8, 16}
. На iPad они {8, 20, 8, 20}
. Итак, теперь мы знаем, что разница в 4 пикселя, которая, скорее всего, не соответствует вашей пользовательской ячейке просмотра таблицы.
Подкласс класса представления таблицы должен адаптировать ограничение левого поля при изменении layoutMargins.
Вот соответствующий фрагмент кода:
- (void)layoutMarginsDidChange
{
[super layoutMarginsDidChange];
self.leftLayoutMarginConstraint.constant = self.layoutMargins.left;
self.rightLayoutMarginConstraint.constant = self.layoutMargins.right;
}
Адаптация к полям макета в коде позволяет всегда получать правильное дополнение для вашей метки названия.
Вы также можете взглянуть на один из моих подклассов UITableViewCell, которые уже уважают макеты макетов: https://github.com/bhr/BHRExtensions/blob/master/BHRExtensions/Utilities/BHRTitleAndValueTableCell.m
Приветствия
Ответ 3
Прочитав существующие ответы и не найдя очевидного программного решения, я еще немного покопался и теперь нашел хороший ответ для всех, кто столкнулся с этой проблемой.
Во-первых, нет необходимости устанавливать preservesSuperviewLayoutMargins
для представления ячейки или представления содержимого, как подразумевают другие ответы. Хотя значением по умолчанию является false
, его изменение на true
не оказало заметного эффекта, который я мог видеть.
Ключом к тому, чтобы заставить это фактически работать, является свойство layoutMarginsGuide
в UIView
. Используя это значение, мы можем легко прикрепить leadingAnchor
любого подпредставления к leadingAnchor
руководства. Вот как это выглядит в коде (и вполне может быть то, что IB делает за кулисами, как в ответе Jonas).
В подклассе UITableViewCell
вы должны сделать что-то вроде этого:
override func updateConstraints() {
let margins = contentView.layoutMarginsGuide
let leading = margins.leadingAnchor
subview1.leadingAnchor.constraintEqualToAnchor(leading).active = true
subview2.leadingAnchor.constraintEqualToAnchor(leading).active = true
super.updateConstraints()
}
Обновление Swift 4.1
override func updateConstraints() {
let margins = contentView.layoutMarginsGuide
let leading = margins.leadingAnchor
subview1.leadingAnchor.constraint(equalTo: leading).isActive = true
subview2.leadingAnchor.constraint(equalTo: leading).isActive = true
super.updateConstraints()
}
Это все! Если вы разрабатываете для версий iOS до iOS 9, вам нужно заменить привязки макетов и использовать вместо них вставку layoutMargins
.
Примечание: я написал библиотеку, чтобы сделать закрепление привязки немного красивее, если вы предпочитаете более чистый синтаксис. Он называется SuperLayout и доступен на Cocoapods. В верхней части исходного файла импортируйте SuperLayout
:
import SuperLayout
А затем в своем блоке макета используйте ~~
, ≤≤
и ≥≥
для закрепления ограничений:
override func updateConstraints() {
let margins = contentView.layoutMarginsGuide
subview1.leadingAnchor ~~ margins.leadingAnchor
subview2.leadingAnchor ~~ margins.leadingAnchor
super.updateConstraints()
}
ios 11+: пусть margins = contentView.directionalLayoutMargins
...
на случай, если вам нужно адаптироваться к LTR и RTL из коробки. Я предполагаю, что большинству людей это нужно.
Ответ 4
Мне удалось получить ячейки с пользовательскими стилями, выровненными со стандартными ячейками, выполнив следующие действия:
- В схеме документа выберите "Просмотр содержимого" для ячейки с
пользовательский стиль.
- Перейдите к Инспектору размеров.
- В раскрывающемся списке "Макет полей" нажмите значок "плюс плюс" рядом с "Сохранить поля надзора".
- Выберите класс размера iPad, который является "Regular Width x Regular Height".
- Установите флажок рядом с надписью "Сохранять поля надзора".
- Разрешить любые предупреждения Auto Layout путем обновления фреймов.
Это работало для меня в Xcode 7; Я надеюсь, что он будет работать и в Xcode 6.
Ответ 5
У меня была эта проблема при тестировании на iPad Air, OS 10.1.1. Заголовки таблицы были отступы намного дальше, чем они должны были быть, и это было еще хуже в альбомной ориентации. Но они были в порядке на iphones до OS 11.
Удивительное решение было следующей строкой кода сразу после создания таблицы (извините, я работаю только на С#, но легко выработать эквиваленты Obj-C и Swift):
myTableView.SeparatorInset = myTableView.SeparatorInset;
Затем все было отступом, как и должно быть!