Неправильная высота представления заголовка таблицы при использовании автоматического макета, IB и размеров шрифта
Я пытаюсь создать представление заголовка для моего uiTableView (не заголовок раздела, у меня уже есть.) Я установил XIB в построителе интерфейса. Все соединения подключены, и все работает прекрасно... за исключением того, что на столе недостаточно места! Моя проблема в том, что верх таблицы немного перекрывает заголовок таблицы.
Мой XIB настроен с автозапуском для всех кнопок, и IB рад, что ограничения не конфликтуют / неоднозначны. Для вида установлено значение Freeform size, которое в моем случае оказалось равным 320 x 471. Затем в ограничениях для представления я установил собственный размер для представления того же самого.
Теперь это прекрасно работает с моим столом. Все выглядит отлично. Но если я вручную изменю любой из шрифтов в представлении заголовка с помощью кода, компоновка увеличит представление и окажется под моей таблицей.
Любые идеи, как заставить tableviewcontroller оставить достаточно места для представления заголовка после установки шрифтов и размеров? Надеюсь, у меня есть смысл объяснять это.
11 ответов
Примечание: версию Swift 3+ можно найти здесь: https://gist.github.com/marcoarment/1105553afba6b4900c10
Идея состоит в том, чтобы рассчитать высоту заголовка с помощью systemLayoutSizeFittingSize:targetSize
,
Возвращает размер представления, которое удовлетворяет ограничениям, которые оно содержит. Определяет наилучший размер представления с учетом всех ограничений, которые он содержит, и ограничений его подпредставлений.
После изменения высоты заголовка необходимо переназначить tableHeaderView
свойство настраивать ячейки таблицы.
Основываясь на этом ответе: Использование Auto Layout в UITableView для динамического размещения ячеек и переменной высоты строк
- (void)sizeHeaderToFit
{
UIView *header = self.tableView.tableHeaderView;
[header setNeedsLayout];
[header layoutIfNeeded];
CGFloat height = [header systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
CGRect frame = header.frame;
frame.size.height = height;
header.frame = frame;
self.tableView.tableHeaderView = header;
}
Я столкнулся с подобной проблемой, когда пытался установить заголовок моего табличного представления на собственное представление, которое я определил с помощью автоматического макета с использованием конструктора интерфейса.
Когда я это сделал, я обнаружил, что это будет скрыто заголовком раздела.
Моя работа включала использование "фиктивного представления" в качестве представления заголовка таблицы, а затем добавление моего пользовательского представления в это фиктивное представление в качестве подпредставления. Я думаю, что это позволяет автоматическому расположению настроить внешний вид согласно ограничениям и рамке содержащего представления. Это, вероятно, не так элегантно, как решение Вокилама, но оно работает для меня.
CGRect headerFrame = CGRectMake(0, 0, yourWidth, yourHeight);
UIView *tempView = [[UIView alloc] initWithFrame:headerFrame];
[tempView setBackgroundColor:[UIColor clearColor]];
YourCustomView *customView = [[YourCustomView alloc] initWithFrame: headerFrame];
[tempView addSubview:movieHeader];
self.tableView.tableHeaderView = tempView;
Потому что ваш tableHeaderView
в начальной форме xib
с автоматической разметкой, так что ограничения вашего обычая headerView
и superView
неизвестно. Вы должны добавить constraints
вашего обычая headerView
:
1.in viewDidLLoad
назначьте свой пользовательский headerView для tableHeaderView tableView
- (void)viewDidLoad
{
[super viewDidLoad];
UIView *yourHeaderView = [[[NSBundle mainBundle] loadNibNamed:@"yourHeaderView" owner:nil options:nil] objectAtIndex:0];
//important:turn off the translatesAutoresizingMaskIntoConstraints;apple documents for details
yourHeaderView.translatesAutoresizingMaskIntoConstraints = NO;
self.tableView.tableHeaderView = yourHeaderView;
}
2.in - (void) updateViewConstraints, добавьте основные ограничения вашего пользовательского headerView.
- (void)updateViewConstraints
{
NSDictionary *viewsDictionary = @{@"headerView":yourHeaderView};
NSArray *constraint_V = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[headerView(121)]"
options:0
metrics:nil
views:viewsDictionary];
NSArray *constraint_H = [NSLayoutConstraint constraintsWithVisualFormat:@"H:[headerView(320)]"
options:0
metrics:nil
views:viewsDictionary];
[self.headerView addConstraints:constraint_H];
[self.headerView addConstraints:constraint_V];
NSArray *constraint_POS_V = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[headerView]"
options:0
metrics:nil
views:viewsDictionary];
NSArray *constraint_POS_H = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[headerView]"
options:0
metrics:nil
views:viewsDictionary];
[self.tableView addConstraints:constraint_POS_V];
[self.tableView addConstraints:constraint_POS_H];
[super updateViewConstraints];
}
ХОРОШО! PS: вот соответствующий документ: изменение размера представлений контроллера представления
Что решило мою проблему, так это:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
sizeHeaderToFit()
}
private func sizeHeaderToFit() {
if let headerView = tableView.tableHeaderView {
headerView.setNeedsLayout()
headerView.layoutIfNeeded()
let height = headerView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
var newFrame = headerView.frame
// Needed or we will stay in viewDidLayoutSubviews() forever
if height != newFrame.size.height {
newFrame.size.height = height
headerView.frame = newFrame
tableView.tableHeaderView = headerView
}
}
}
Я нашел это решение где-то и работал как шарм, хотя я не могу вспомнить, где.
Я нашел ответ вокилама, чтобы отлично работать. Вот его решение в Swift.
func sizeHeaderToFit() {
guard let header = tableView.tableHeaderView else { return }
header.setNeedsLayout()
header.layoutIfNeeded()
let height = header.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
header.frame.size.height = height
tableView.tableHeaderView = header
}
Другие ответы указали мне правильное направление для правильного изменения размера tableHeaderView. Тем не менее, я получил разрыв между заголовком и ячейками tableView. Это также может привести к тому, что ваши ячейки будут перекрываться с вашим заголовком, в зависимости от того, увеличился или уменьшился заголовок.
Я пошел из этого:
tableView.reloadData()
let height = tableViewHeader.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
tableView.tableHeaderView?.frame.size.height = height
Чтобы исправить разрыв / перекрытие, все, что мне нужно было сделать, это переместить tableView.reloadData() после того, как я установил новую высоту вместо ранее.
К этому:
let height = tableViewHeader.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
tableView.tableHeaderView?.frame.size.height = height
tableView.reloadData()
В моем случае systemLayoutSizeFittingSize
верните неправильный размер, потому что одно из подпредставлений использует соотношение сторон (ширина к высоте) с ограничением 4:1.
После того, как я изменил его на постоянное значение ограничения высоты (100), он начинает работать как положено.
Объединение ответов @SwiftArchitect, @vokilam и @mayqiyue и использование программно созданных tableHeaderView
вместо пера (хотя я не вижу причин, по которым он не должен работать с пером), и под iOS 10.x/Xcode 8.2.1 вот что у меня работает:
Создайте представление в вашем контроллере представления -viewDidLoad
или же -init
методы, или где вы используете табличное представление:
MyCustomTableHeaderView *myCustomTableHeaderView = [[MyCustomTableHeaderView alloc] init]; // Don't set its frame
myCustomTableHeaderView.translatesAutoresizingMaskIntoConstraints = NO;
self.myTableView.tableHeaderView = myCustomTableHeaderView;
self.myCustomTableHeaderView = myCustomTableHeaderView; // We set and update our constraints in separate methods so we store the table header view in a UIView property to access it later
Везде, где вы устанавливаете ограничения вашего пользовательского представления заголовка (может быть в `updateConstraints, но этот метод может вызываться несколько раз):
// We use Pure Layout in our project, but the workflow is the same:
// Set width and height constraints on your custom view then pin it to the top and left of the table view
// Could also use VFL or NSLayoutConstraint methods
[self.myCustomTableHeaderView autoSetDimension:ALDimensionWidth toSize:CGRectGetWidth(self.superview.frame)]; // Width set to tableview's width
[self.myCustomTableHeaderView autoSetDimension:ALDimensionHeight toSize:height]; // Whatever you want your height to be
[self.myCustomTableHeaderView autoPinEdgeToSuperviewEdge:ALEdgeTop];
[self.myCustomTableHeaderView autoPinEdgeToSuperviewEdge:ALEdgeLeft];
// This is important
[self.myCustomTableHeaderView layoutIfNeeded]; // We didn't need to call -setNeedsLayout or reassign our custom header view to the table view's tableHeaderView property again, as was noted in other answers
Ответ @rmigneco работал для меня, так что вот его быстрая версия:
let headerFrame = CGRectMake(0, 0, 100, 1);
let tempView = UIView(frame: headerFrame);
tempView.backgroundColor = UIColor.clearColor()
let yourCustomView = CustomView(frame: headerFrame)
tempView.addSubview(yourCustomView)
self.tableView.tableHeaderView = tempView
sizeHeaderToFit
предложенный @vokilam не работает для меня.
То, что сработало, является модифицированной версией @mayqiyue, вызываемой во время viewDidLoad
, проводная высота, равная ширине вида таблицы.
// Anchor the headerview, and set width equal to superview
NSDictionary *viewsDictionary = @{@"headerView":self.tableView.tableHeaderView,
@"tableView":self.tableView};
[self.tableView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[headerView]"
options:0
metrics:nil
views:viewsDictionary]];
[self.tableView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[headerView]"
options:0
metrics:nil
views:viewsDictionary]];
[self.tableView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[headerView(==tableView)]"
options:0
metrics:nil
views:viewsDictionary]];
[self.tableView.tableHeaderView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[headerView(121)]"
options:0
metrics:nil
views:viewsDictionary]];
Ниже код работал для меня:- Добавьте это в свой класс UIView -
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
self.frame = CGRectMake(0, 0, self.frame.size.width, [[self class] height]);
}
return self;
}